官网上有什么是 TypeScript,为什么要用 TypeScript ,和 api 相关的文档,而且还是一手的学习资料,无疑是最好的学习资料了。
网站 | 说明 |
---|---|
TypeScript | TypeScript 官网,TS 扩展了 JavaScript ,为它添加了类型支持 |
typescript-tutorial | TypeScript 入门教程,循序渐进的理解 TypeScript |
TypeScript 手册 | TypeScript 使用手册 |
typescript-book-chinese | 深入理解 TypeScript |
clean-code-typescript | 适用于TypeScript的简洁代码概念 |
TypeScript入门 | TypeScript 入门的视频教程 |
typescript-tutorial | TypeScript 速成教程(2小时速成) |
TypeScript 高级技巧
用了一段时间的 typescript 之后,深感中大型项目中 typescript 的必要性,它能够提前在编译期避免许多 bug,如很恶心的拼写问题。而越来越多的 package 也开始使用 ts,学习 ts 已是势在必行
1、keyof keyof
与 Object.keys
略有相似,只不过 keyof
取 interface
的键
interface Point {
x: number;
y: number;
}
// type keys = "x" | "y"
type keys = keyof Point;
可以使用 keyof 来加强 get 函数的类型功能
function get<T extends object, K extends keyof T>(o: T, name: K): T[K] {
return o[name]
}
2、 Required & Partial & Pick 这个都是typescript
内置的函数
type Partial<T> = {
[P in keyof T]?: T[P];
};
type Required<T> = {
[P in keyof T]-?: T[P];
};
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
interface User {
id: number;
age: number;
name: string;
};
// 相当于: type PartialUser = { id?: number; age?: number; name?: string; }
type PartialUser = Partial<User>
// 相当于: type PickUser = { id: number; age: number; }
type PickUser = Pick<User, "id" | "age">
3、Condition Type 类似于 js 中的 ?: 运算符,可以使用它扩展一些基本类型
T extends U ? X : Y
type isTrue<T> = T extends true ? true : false
// 相当于 type t = false
type t = isTrue<number>
// 相当于 type t = false
type t1 = isTrue<false>
4、 never & Exclude & Omit
结合 never 与 conditional type 可以推出很多有意思而且实用的类型
type Exclude<T, U> = T extends U ? never : T;
// 相当于: type A = 'a'
type A = Exclude<'x' | 'a', 'x' | 'y' | 'z'>
结合 Exclude 可以推出 Omit 的写法
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
interface User {
id: number;
age: number;
name: string;
};
// 相当于: type PickUser = { age: number; name: string; }
type OmitUser = Omit<User, "id">
5、 typeof 顾名思义,typeof 代表取某个值的 type,可以从以下示例来展示他们的用法
6、 is 使用 is 来判定值的类型
export function isType(type: any): type is GraphQLType;
export function isScalarType(type: any): type is GraphQLScalarType;
export function isObjectType(type: any): type is GraphQLObjectType;
export function isInterfaceType(type: any): type is GraphQLInterfaceType;
7、 interface & type interface 可以如下合并多个,而 type 只能使用 & 类进行连接。
8、 Record & Dictionary & Many typescript
语法糖
type Record<K extends keyof any, T> = {
[P in K]: T;
};
interface Dictionary<T> {
[index: string]: T;
};
interface NumericDictionary<T> {
[index: number]: T;
};
const data:Dictionary<number> = {
a: 3,
b: 4
}
9、 enum 维护常量表
const enum TODO_STATUS {
TODO = 'TODO',
DONE = 'DONE',
DOING = 'DOING'
}
function todos (status: TODO_STATUS): Todo[];
todos(TODO_STATUS.TODO)
巧用 Typescript(一)
巧用注释 通过/** */形式的注释可以给 TS 类型做标记,编辑器会有更好的提示:
注释有很多规范的字段 ,但不用着急翻文档,在 /** */ 里输入 @ 就可以看到丰富的选择
/** A cool guy. */
interface Person {
/** A cool name. */
name: string,
}
巧用 typeof
我们一般先写类型,再使用:
interface Opt {
timeout: number
}
const defaultOption: Opt = {
timeout: 500
}
有时候可以反过来:
const defaultOption = {
timeout: 500
}
type Opt = typeof defaultOption
巧用联合类型
// Not good.
interface Dinner1 {
fish?: number,
bear?: number,
}
// Awesome!
type Dinner2 = {
fish: number,
} | {
bear: number,
}
巧用查找类型
interface Person {
addr: {
city: string,
street: string,
num: number,
}
}
当需要使用 addr 的类型时,除了把类型提出来
interface Address {
city: string,
street: string,
num: number,
}
interface Person {
addr: Address,
}
还可以
Person["addr"] // This is Address.
巧用查找类型+泛型+keyof
interface API {
'/user': { name: string },
'/menu': { foods: Food[] },
}
const get = <URL extends keyof API>(url: URL): Promise<API[URL]> => {
return fetch(url).then(res => res.json())
}
极大的增强了代码的提示性
巧用显式泛型
$('button') 是个 DOM 元素选择器,可是返回值的类型是运行时才能确定的,除了返回 any ,还可以
function $<T extends HTMLElement>(id: string):T {
return document.getElementById(id)
}
// Tell me what element it is.
$<HTMLInputElement>('input').value
巧用 DeepReadonly
type DeepReadonly<T> = {
readonly [P in keyof T]: DeepReadonly<T[P]>;
}
const a = { foo: { bar: 22 } }
const b = a as DeepReadonly<typeof a>
b.foo.bar = 33 // Hey, stop!
巧用 Omit
import { Button, ButtonProps } from './components/button'
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
type BigButtonProps = Omit<ButtonProps, 'size'>
function BigButton(props: BigButtonProps) {
return Button({ ...props, size: 'big' })
}
巧用Record类型
业务中,我们经常会写枚举和对应的映射:
type AnimalType = 'cat' | 'dog' | 'frog';
const AnimalMap = {
cat: { name: '猫', icon: ' '},
dog: { name: '狗', icon: ' ' },
forg: { name: '蛙', icon: ' ' },
};
注意到上面 forg 拼错了吗?Record 可以保证映射完整:
type AnimalType = 'cat' | 'dog' | 'frog';
interface AnimalDescription { name: string, icon: string }
const AnimalMap: Record<AnimalType, AnimalDescription> = {
cat: { name: '猫', icon: ' '},
dog: { name: '狗', icon: ' ' },
forg: { name: '蛙', icon: ' ' }, // Hey!
};
如果你喜欢用 enum ,写法也一样的
enum AnimalType {
CAT = 'cat',
DOG = 'dog',
FROG = 'frog',
}
const AnimalMap: Record<AnimalType, AnimalDescription>
巧用Partial
就是部分的意思,很常用的。
const mergeOptions = (options: Opt, patch: Partial<Opt>) {
return { ...options, ...patch };
}
class MyComponent extends React.PureComponent<Props> {
defaultProps: Partial<Props> = {};
}
巧用tsx+extends
在 .tsx 文件里,泛型可能会被当做 jsx 标签
const toArray = <T>(element: T) => [element]; // Error in .tsx file.
加 extends 可破
const toArray = <T extends {}>(element: T) => [element]; // No errors.
巧用ClassOf
有时候,我们要传入类本身,而不是类的实例
abstract class Animal extends React.PureComponent {
/* Common methods here. */
}
class Cat extends Animal {}
class Dog extends Animal {}
// `AnimalComponent` must be a class of Animal.
const renderAnimal = (AnimalComponent: Animal) => {
return <AnimalComponent/>; // WRONG!
}
上面的代码是错的,因为 Animal 是实例类型,不是类本身。应该
interface ClassOf<T> {
new (...args: any[]): T;
}
const renderAnimal = (AnimalComponent: ClassOf<Animal>) => {
return <AnimalComponent/>; // Good!
}
renderAnimal(Cat); // Good!
renderAnimal(Dog); // Good!