typescriptlang 笔记
目录
- 1. Get Started
- 2. Handbook
- 3. Reference
- 3.1. 内置的工具类型
- 3.1.1. Awaited<T>
- 3.1.2. Partial<T>
- 3.1.3. Required<T>
- 3.1.4. Readonly<T>
- 3.1.5. Record<Keys, T>
- 3.1.6. Pick<T, Keys>
- 3.1.7. Omit<T, Keys>
- 3.1.8. Exclude<UnionType, ExcludeMembers>
- 3.1.9. Extract<Type, Union>
- 3.1.10. NonNullable<T>
- 3.1.11. Parameters<T>
- 3.1.12. ConstructorParameters<T>
- 3.1.13. ReturnType<T>
- 3.1.14. InstanceType<T>
- 3.1.15. ThisParameterType<T>
- 3.1.16. OmitThisParameter<T>
- 3.1.17. ThisType<T>
- 3.1.18. Uppercase<StringType>
- 3.1.19. Lowercase<StringType>
- 3.1.20. Capitalize<StringType>
- 3.1.21. Uncapitalize<StringType>
- 3.2. Cheat Sheet
- 3.3. 装饰器
- 3.1. 内置的工具类型
1. Get Started
- typescript 给 js 添加了类型系统
- ts 扩展了基本类型
any
,unknown
,never
,void
2. Handbook
2.1. The TypeScript Handbook
ts 的目的是给 js 提供静态类型检测
2.2. The Basic
2.2.1. 静态类型检测
- 静态类型系统描述了程序运行时 值 该有的行为和形状
- 静态类型检测可以在程序运行前发现类型异常
2.2.2. Non-exception Failure
ts 可以捕获如下不会引起异常的错误
const user = { name: "Daniel", age: 26, }; user.location; // 不存在的变量 user.name(); // 不可调用的函数 const v = Math.random() > 0.5 ? 'a' : 'b' // 基本逻辑错误 if (v != 'a') { // } else if (v == 'b') { // }
2.2.3. 作为工具
- 提供补全功能
- 重构
- 跳到定义
- 查找引用
2.2.4. tsc编译器
2.2.5. Emitting with Errors
使用 tsc
时, 加上 tsc --noEmitWithError
那么有异常时, 不会生成新的内容
2.2.6. 显式类型
2.2.7. 擦除类型
ts 编译后, 会把类型注解擦除掉
2.2.8. 降级
tsc --target esnext
可以编译成兼容指定 es 版本的代码
2.3. 日常使用的类型
2.3.1. 基础类型 string, number, boolean
- 与 js 一致
- 不要使用
String
Number
Boolean
, 虽然他们是合法的
2.3.2. Arrays
let a: number[] = [1] let b: Array<String> = ["h"]
2.3.3. any
- 如果想跳过某个值的类型检查时,可以指定它为
any
- 如果编译器不能推测出对应值的类型,那么会将它设置成
any
, 使用 noImplicitAny 禁用此行为
2.3.4. 变量上的类型注解
let a: string = "Hello"
大部分情况不需要加上这个注解
2.3.5. 函数
function foo(name: string): string { return name + "!" } let b = ["a", "b"] b.forEach((s) => console.log(s.toUpperCase()))
- 注解参数
- 注解返回值
- 通过
contextual typing
推测匿名函数的类型注解
2.3.6. 对象类型
function printName(obj: { first: string; last?: string }): void { console.log(typeof obj.last === 'undefined' ? obj.first : obj.last) }
last?
属于可选属性
2.3.7. Union Types
function welcome(people: string[] | string) { if (Array.isArray(people)) { console.log("Welcome", people.join('and')) } else { console.log('Welcome: ' + people) } } function get3(s: number[] | string) { return s.slice(0, 3) }
- 可使用条件表达式, 获取分流指定的类型
- 可以访问 union type 的公共属性
2.3.8. 类型别名
type ID = number | string type Point = { x: number; y: number; } type Str = string
Note that aliases are only aliases - you cannot use type aliases to create different/distinct “versions” of the same type
2.3.9. Interfaces
interface Point { x: number; y: number }
- interface 和 type 的区别
- 扩展属性
interface P { name: string } interface C extends P { ho: string } type TP = { name: string } type TC = TP & { ho: string } let a: C; let b: TC a.ho b.ho
- 添加字段
type
不能给已有类型添加字段
interface A { a: string } interface A { b: string } let a: A a.b type W = { a: string } type W = { b: string } let aa: W aa.b // no Exist
- 扩展属性
2.3.10. 类型断言
function foo(p) { (p as string).toLocaleUpperCase() }
类型断言只能将类型转换成更精确或更不精确的版本, 所以下面行为是错误的
const x = "hello" as number
但是可以通过下面方式绕过
const x = ("hello" as unknown) as number
2.3.11. 字面量类型
function foo(align: 'left' | 'right') { } type Pos = 1 | 2
- 字面量推测
function handleRequest(url: string, method: 'GET' | 'POST') { } const req = { url: "www.baidu.com", method: 'GET' } handleRequest(req.url, req.method) // wrong. Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'. // 可通过下面方式修复 const req1 = { url: "www.baidu.com", method: 'GET' } as const; handleRequest(req1.url, req1.method) const req2 = { url: "www.baidu.com", method: "GET" as "GET" }; handleRequest(req2.url, req2.method); handleRequest(req.url, req.method as "GET");
2.3.12. null 和 undefined
通过 strictNullChecks 严格检测空值
declare const loggedInUsername: string; const users = [ { name: 'Oby', age: 12 }, { name: 'Heera', age: 32 }, ]; const loggedInUser = users.find((u) => u.name === loggedInUsername); // 如果开启 strictNullChecks, 下面会报错 Object is possibly 'undefined'. console.log(loggedInUser.age); // 在开启状态下,可以通过强制的非空断言绕过 console.log(loggedInUser!.age)
2.3.13. 枚举
2.3.14. 少用的基础类型
const a: bigint = 10n const x1 = Symbol('name') const x2 = Symbol('name') if (x1 === x2) { // 可以识别出此类错误 // This condition will always return 'false' since the types 'typeof x1' and 'typeof x2' have no overlap }
2.4. 类型收缩 Narrowing
2.4.1. typeof 类型守卫
使用 typeof
把变量收缩指指定类型
function printAll(strs: string | string[]) { if (typeof strs === 'string') { console.log(strs.repeat(2)) } else { console.log(strs.join(',')) } }
2.4.2. truthiness 收缩
function printAll(strs: string | string[] | null) { if (strs && typeof strs === 'object') { console.log(strs.join(',')) } else if (typeof strs === 'string') { console.log(strs.repeat(2)) } }
2.4.3. 等式收缩
function foo(x: string | number, y: string | null) { if (x === y) { // 可以推测出 x,y 都为 string console.log(x.repeat(1)) } }
2.4.4. in
收缩
type Fish = { swim: () => void } type Bird = { fly: () => void } type Human = { swim?: () => void; fly?: () => void } function move(animal: Fish | Bird | Human) { if ('swim' in animal) { // 可以推测是 animal 为 Fish | Human } else { // 可以推测是 animal 为 Bird | Human } }
2.4.5. instanceof
收缩
class Foo { print() { } } function bar(x: Foo | string) { if (x instanceof Foo) { // 如果 x 的原型链上有 Foo.prototype x.print() } else { x.repeat(2) } }
2.4.6. 赋值
let x = Math.random() < 0.5 ? 10 : 'hello' x = true; // wrong, 因为 x 声明为 number|string
2.4.7. 控制流分析
2.4.8. 类型预测
使用 param is Type
的模式,收缩变量的类型
type Fish = { swim: () => void } type Bird = { fly: () => void } function isFish(animal: Fish | Bird): animal is Fish { return (animal as Fish).swim !== undefined } function foo(animal: Fish | Bird) { if (isFish(animal)) { animal.swim() } else { animal.fly() } } function getFishs(animal: (Fish | Bird)[]): Fish[] { return animal.filter(isFish) }
2.4.9. 区分联合类型
interface Circle { kind: 'circle' radius: number } interface Square { kind: 'square', sideLength: number } type Shape = Circle | Square function area(shape: Shape) { if (shape.kind === 'circle') { // 可以推测出 shape 为 Cicle return Math.PI * shape.radius ** 2 } else { // 可以推测出 shape 为 Square return shape.sideLength ** 2 } } area({ kind: 'circle', // 输入 cicle时, 补全会过滤掉非 Circle 的属性 radius: 10, })
2.4.10. never
当收缩联合类型时, 剔除掉所有可能的类型后,ts 会把变量标记为 never
function foo(x: string | number) { if (typeof x === 'string') { // } else if (typeof x === 'number') { // } else { // x 为 never } }
2.4.11. 使用 never
穷尽检测
任意的类型都可以赋值给 never
function foo(x: string | number) { if (typeof x === 'string') { // } else if (typeof x === 'number') { // } else { // 假如 x 改为 string|number|null, 那么ts 会报错。 // 用此种方式可以保证后续给类型添加成员时,所以分支情况都考虑到 const _exhaustiveCheck: never = x; } }
2.5. 函数
2.5.1. 函数类型表达式
function foo(fn: (a: string) => void) { } type Callback = (a: string) => void type C2 = (a) => void; // a 为 any
如果没有指定参数类型, 那么其为 any
2.5.2. 给函数添加属性
type FnWithDesc = { desc: string; (arg: string): string; // 注意 不是 => 而是 : } function foo(fn: FnWithDesc) { return fn.desc + fn("hello") }
2.5.3. 构造器签名
interface Some { foo: string } // 既可以直接调用,又可以作为构造器 type SomeContruct = { new(arg: string): Some; (arg: string): Some } // 等同上述的 interface Some2Contruct { new(arg: string): Some; (arg: string): Some } function foo(C: SomeContruct, C2: Some2Contruct) { let c1 = new C('c') c1.foo let c2 = C('c') c2.foo let c3 = new C2('') c3.foo let c4 = C2('') c4.foo }
2.5.4. 泛型函数
function first<T>(arr: T[]): T | undefined { return arr[0] } let a = first(['1']) // string let b = first([2]) // number let c = first[] // undefined
2.5.5. 可选参数
function foo(x?: number) { }
尽量不要给回调函数添加可选参数签名,如下
function foreach<T>(arr: T[], callback: (a: T, index?: number) => void) { for (let i = 0; i < arr.length; i++) { callback(arr[i], i) } } foreach([], (ele, idx) => { console.log(idx.toFixed(10), ele) // Object(idx) is possibly 'undefined' })
函数调用多传参数, ts 不会报错的
2.5.6. 函数重载
// 重载签名 function makeDate(ts: number): Date; function makeDate(m: number, d: number, y: number): Date; // 实现签名 // 注意返回值也写上,与上述签名保持一致 function makeDate(mOrTs: number, d?: number, y?: number): Date { if (d !== undefined && y !== undefined) { return new Date(y, mOrTs, d); } else { return new Date(mOrTs); } } makeDate(10); makeDate(10, 12, 34); makeDate(10, 23); // error
外部只能看到重载签名, 不能看到实现签名
实现签名 必须和 重载签名 兼容 , 包括参数签名和返回值
function fn(x: number): number; function fn(x: string): string; // wrong function fn(x: number): number { return x } function fn1(x: number): number; function fn1(x: string): boolean; // wrong function fn1(x: number | string) { return 1 }
尽可能用联合类型替代重载
2.5.7. 声明 this
interface User { admin: string } interface DB { filterUsers(filters: (this: User) => boolean): User[] } function foo(db: DB) { // 注意不要使用箭头函数 let users = db.filterUsers(function () { return this.admin === 'admin' }) }
2.5.8. 其他涉及函数的类型
2.5.9. 剩余参数
function add(x: number, ...rest: number[]): number { return x + rest.reduce((a, b) => a + b, 0) }
2.5.10. 参数解构
function foo({ a, b }: { a: number, b: string }) { return b.length + a }
2.5.11. void
返回值
当函数的类型定义返回值是 void
时,
- 其他有返回值的函数,也可以赋值给该类型
- 函数调用的结果为
void
type Fn = () => void let f1: Fn = () => true let f2: Fn = () => 1 let a1 = f1() // void let a2 = f2() // void
如果是函数实现中使用 void
那么就必须匹配
function foo(): void { return 1; // wrong }
2.6. 对象类型
2.6.1. 属性修饰符
- 可选属性
interface Shape { xPos: number yPos?: number }
- 只读属性
interface P { readonly name: string } interface P2 { name: string } let p: P = { name: "Hi" } let p2: P2 = p; // 可以改变 p 的值 p2.name += ' age'
readonly
表示该属性不能重新写入,如果其值是对象,不会影响该对象的属性的写入- 可以通过别名的方式,更改
readonly
的属性的值
- 索引签名
// 索引类型只能是 string 或 number interface Arr { [index: number]: string } interface Arr1 { [index: string]: number[] } // 如果同时存在 string 或 number 索引, // number 索引的类型必须与 string 的兼容 interface Arr2 { [index: number]: string [other: string]: string | number } interface S0 { // 只读 readonly [name: string]: string } // 如果同时存在索引属性和具名属性 // 那么具名属性的类型 必须与 string 的索引属性的类型兼容 interface S2 { [name: string]: string | number length: number val: string }
2.6.2. 扩展类型
interface P { name: string, tmp: any[] } interface C extends P { age: number, tmp: string[] } let c: C = { name: 'name', age: 1, tmp: ['str'] }
如果扩展已有的属性,那么其类型必须与父级的类型的子集
- 交叉类型
interface Colorful { color: string } interface Circle { radius: number, list: number[] } interface Other { list: object } type ColorfulCircle = Colorful & Circle & Other let c: ColorfulCircle = { color: "0xffffff", radius: 12, // number[] & object list: [12] }
&
产生新的类型, 会合并所有的属性- 如果两个类型有相同名字的属性, 那么其类型也是使用
&
交叉类型和
extends
不同在于对于冲突的属性的处理方式, 交叉类型会将两个类型交叉,extends
则是子类型属性的类型是父类型属性的类型的子集
2.6.3. 泛型对象类型
interface Box<T> { contents: T } type B<T> = { contents: T } type OrNull<T> = T | null
2.6.4. 只读数组
function foo(arr: readonly string[]) { // 不能使用 push, sort 等做改变该数组的操作 // 不能重新赋值给元素 let a: string[] = arr; // wrong, 不能赋值给非只读数组 } // 等同上 function bar(arr: ReadonlyArray<string>) { }
typescript 特有, 运行时没有这种类型
2.6.5. 元组
type Pair = [string, number] type OptionalTuple = [string, number?] type StringNumbers = [string, ...number[]] type StringsNumber = [...string[], number] type StringBoolsNumber = [string, ...boolean[], number]
2.7. 类型操作 - 从类型中创建类型
2.7.1. 泛型
- 泛型方法
function id<T>(arg: T): T { return arg; } let id1: <T>(a: T) => T = id; let id2: { <T>(a: T): T } = id; interface IdFn { <T>(a: T): T; } let id3: IdFn = id; interface IdFn2<T> { (a: T): T; } let id4: IdFn2<number> = id; function id<T>(arg: T): T { return arg } let id1: <T>(a: T) => T = id let id2: { <T>(a: T): T } = id
没有泛型
enum
和 泛型namespace
- 泛型类
class My<T> { static p1: T; // wrong prop1: T; }
泛型只作用与类的实例部分,不能作用于 static 部分
- 泛型约束
interface LengthWise { length: number } function foo<T extends LengthWise>(arg: T): number { return arg.length } foo({ length: 3, a: 23 })
- 使用类型参数
- 使用泛型类
工厂函数
class Animal { name = 'animal'; } class Bird extends Animal { fly() { } } class Fish extends Animal { swim() { } } function instance<T extends Animal>(arg: new () => T): T { return new arg(); } instance(Fish).swim(); instance(Bird).fly();
2.7.2. keyof
interface Point { x: number; y: number } type P = keyof Point let p: P = 'x' // 'x' 'y' interface A { [index: number]: string } type PA = keyof A // number interface B { [index: string]: string } type PB = keyof B // number | string
2.7.3. typeof
function f() { return { x: 1, ops: 'hello' } } type F = ReturnType<typeof f> let ff: F = { x: 23, ops: 'name' }
typeof
仅作用于 标识符和它的属性
2.7.4. 索引访问符
interface Person { name: string, age: number } type Name = Person['name'] // string let list = [{ name: 'Mark', age: 10 }] type P = typeof list[number] let p: P = { name: 'name', age: 9 } const pp = 'age'; type PPP = Person[pp]; // wrong 不能用变量 type Age = 'age' type PPP1 = Person[Age] // 可以用 类型别名. number
2.7.5. 条件类型
interface IdLabel { id: number; } interface NameLabel { name: string; } type Label<T extends number | string> = T extends number ? IdLabel : NameLabel; function createLabel<T extends number | string>(arg: T): Label<T> { return ( typeof arg === 'number' ? { id: arg, } : { name: arg } ) as Label<T>; } let n = createLabel('hello'); n.name; let a = createLabel(10); a.id; interface IdLabel { id: number } interface NameLabel { name: string } type Label<T extends number | string> = T extends number ? IdLabel : NameLabel function createLabel<T extends number | string>(arg: T): Label<T> { return typeof arg === 'number' ? { id: arg } : { name: arg } } let n = createLabel('hello') n.name let a = createLabel(10) a.id
根据函数输入参数的类型决定返回类型
使用条件类型进行约束
type MessageOf<T> = T extends { message: unknown } ? T['message'] : never type A = MessageOf<{ message: string[] }> // string[] type Flatten<T> = T extends any[] ? T[number] : T type B = Flatten<number[]> // number type C = Flatten<number> // number
infer
type Flatten<T> = T extends Array<infer Item> ? Item : T type A = Flatten<string[]> // string type ReturnType0<T> = T extends () => infer Return ? Return : never type B = ReturnType0<() => number> // number
infer
会产生一个新的类型变量
当
infer
有多个签名的函数时, 会使用最后一个的类型declare function stringOrNum(x: string): number; declare function stringOrNum(x: number): string; declare function stringOrNum(x: string | number): string | number; type T1 = ReturnType<typeof stringOrNum>; // string | number
对于联合类型, 会自动分配
type ToArray<T> = T extends any ? T[] : never type A = ToArray<number | string> // number[] | string[] type ToArrayNoDist<T> = [T] extends [any] ? T[] : never type B = ToArrayNoDist<number | string> // (number|string)[]
2.7.6. Mapped Types
- 修改某个类型的属性的类型
type OptionFlag<T> = { [Prop in keyof T]: boolean } interface Animal { fly(): void; name: string } type Ops = OptionFlag<Animal> let a: Ops = { fly: true, name: false }
- 更变修饰符
interface ReadonlyAccount { readonly name: string readonly id: number time?: Date } type RmModifier<T> = { -readonly [P in keyof T]-?: T[P] } type LooseAccount = RmModifier<ReadonlyAccount> // { // name: string; // id: string; // time: Date // }
- 重新映射类型
- 重命名属性
type Getters<T> = { [Prop in keyof T as `get${Capitalize<Prop & string>}`]: () => T[Prop]; }; interface Person { name: string; age: number; } type GetPerson = Getters<Person>; let p: GetPerson = { getName() { return ''; }, getAge() { return 1; }, };
- 过滤某个属性
type RmKind<T> = { [Prop in keyof T as Exclude<Prop, 'kind'>]: T[Prop]; }; interface Foo { kind: 'hello'; age: 10; } type FooWithoutKind = RmKind<Foo>; let a: FooWithoutKind = { age: 10, };
- 映射并集
type EventConfig<Events extends { kind: string }> = { [E in Events as E['kind']]: (event: E) => void; }; type ClickEvt = { kind: 'click'; x: number; }; type MoveEvt = { kind: 'move'; y: number; }; type Config = EventConfig<ClickEvt | MoveEvt>; let c: Config = { click(e) { e.x; }, move(e) { e.y; }, };
- 重命名属性
- 模板字面量类型
- 构建事件
type ToEventSource<T> = { on(eventName: `${string & keyof T}Changed`, cb: (event: any) => void): void; }; function makeWatch<T>(obj: T): ToEventSource<T> { throw ''; } let p = makeWatch({ firstName: 'Mery', lastName: 'John' }); p.on('firstNameChanged', () => 1); p.on('lastNameChanged', () => 2);
- 上述的,根据名字指定
cb
的参数类型
type ToEventSource<T> = { on<Key extends string & keyof T>(eventName: `${Key}Changed`, cb: (event: T[Key]) => void): void; }; function makeWatch<T>(obj: T): ToEventSource<T> { throw ''; } let p = makeWatch({ firstName: 'Mery', lastName: 'John', age: 2 }); p.on('firstNameChanged', (e) => { typeof e === 'string'; }); p.on('ageChanged', (e) => { typeof e === 'number'; });
- 内置的操作 StringType 的类型
- Uppercase<StringType>
type A = 'hello, wor' type B = Uppercase<A>; // 'HELLO, WOR'
- Lowercase<StringType>
type A = 'HELL' type B = Lowercase<A> // 'hell
- Capitalize<StringType>
type A = 'hello' type B = Capitalize<A> // 'Hello'
- Uncapitalize<StringType>
type A = 'HLL' type B = Uncapitalize<A> // 'hLL'
内部实现原理是使用了 js 的
toUpperCase
和toLowerCase
- Uppercase<StringType>
- 构建事件
2.8. 类
2.8.1. 类成员
- 字段
class Greeter { x: number; y = 1; z; readonly aa = 1; }
- strictPropertyInitialization
如果开启了该 flag 那么字段必须被初始化, (在构造器或字段声明位置)
class Ok { x: number; // wrong y: number; z: number; // wrong aa!: number; // 加个 ! 可以绕过 strictPropertyInitialization constructor() { this.y = 1 // 因为方法可能被子类重写,所以在通过调用方法来初始化字段不能绕过 this.setZ() } setZ() { this.z = 1 } }
- 构造器
class Base { x: number } class Child extends Base { constructor() { super() this.x = 1 } }
- 子类使用
this
前必须调用super()
- 构造器没有泛型和返回类型注解
- 子类使用
- 方法
class Foo { x = 1 print(): void { console.log(this.x) } }
- Getters/Setters
class Thing { _size = 1; get size() { return this._size; } set size(v: string | number) { this._size = Number(v); } _x = 2; get x() { return this._x; } set x(v) { // v 被推测成 number } }
- 如果有
get
没有set
那么会变成readonly
- 如果
set
没有指定类型, 那么会推测成与get
一致 get
和set
需要有相同的可见性
- 如果有
- 索引签名
class Foo { [name: string]: boolean | (() => void) print() { } x = true }
因为涉及到 索引签名 与其他成员的兼容性问题, 所以一般不怎么使用
2.8.2. 继承
implements
interface A { x: number; y?: number; } interface B { foo(): void; } class C implements A, B { x = 1; foo() { // } }
- 可以实现多个接口
implements
只是检查 class 是否满足接口, 接口的类型不会影响到 class 的成员的类型
extends
- 重写
- 重写方法
class Base { greet() { console.log('hello'); } } class Derived extends Base { greet(name?: string): void { super.greet(); console.log(name); } }
重写的方法, 签名必须兼容父类的方法,不然编译不通过
- 重写字段
如果只是想指定子类字段类型为父类的子集,而不是重新声明,可以使用
declare
class Base { x: number | string } class Derived extends Base { declare x: string }
- 重写方法
- 初始化顺序
依据 js 的定义
- 父类字段
- 父类构造器
- 子类字段
- 子类构造器
- 继承内置类型
如果要转义成 ES5 的代码, 继承内置类型时,使用如下方式, 不然原型链会缺失当前的类 (
MsgError
)class MsgError extends Error { constructor(m: string) { super(m); // Set the prototype explicitly. Object.setPrototypeOf(this, MsgError.prototype); } sayHello() { return "hello " + this.message; } } #+end_#+begin_src
2.8.3. 成员可视性
public
默认
protected
可以通过继承改变对应字段的访问性
class Base { protected x = 1 } class Derived extends Base { x = 2 } new Derived().x
class Base { protected x = 1; } class Derived2 extends Base { f1(other: Derived2) { other.x = 10; } f2(other: Base) { other.x = 10; // wrong } }
不能通过父类的引用访问
protected
成员
private
ts 运行跨实例访问
private
成员class A { private x = 10; public sameAs(other: A) { // No error return other.x === this.x; } }
成员可视性只运用于编译阶段,如果要实现真正的私有字段,使用
#
,或者WeakMap
(当使用#
编译成js时,ts就是使用WeakMap
)
2.8.4. 静态成员
class Base { static x = 1 // 添加可视性 private static y = 2 } // 可继承 class Derived extends Base { } Base.x Derived.x Base.y // wrong
对于 name
, length
, call
不能使用 static
字段
ts 没有静态类
2.8.5. 泛型类
class A<T>{ content: T constructor(c: T) { this.content = c } } let a = new A('') a.content // string
静态成员不能引用类的泛型参数
2.8.6. this
- 箭头函数
class MyClass { name = "My" getName = () => this.name } new MyClass().getName() // My
- 使用箭头函数可以不丢失
this
- 每个实例都会有一个 该函数的副本, 所以会有更多的内存消耗
- 子类不能使用
super.getName
访问父类的箭头函数
- 使用箭头函数可以不丢失
this
参数
class MyClass { name = "MyClass"; getName(this: MyClass) { return this.name; } } const c = new MyClass(); // OK c.getName(); // Error, would crash const g = c.getName; console.log(g());
- 可以通过该方法,在编译时校验调用时的
this
指向 - 实例共享方法,所以不会有额外的内存消耗
- 子类可以使用
super.getName
- 可以通过该方法,在编译时校验调用时的
this
类型
使用
this
类型,可以动态引用当前类型
class Box { add(box: this) {} } class ChildBox extends Box { content: string; } let child = new ChildBox(); let base = new Box(); child.add(base);
this is Type
可以用来收缩成具体的实例
class FSObj { isFile(): this is FileObj { return this instanceof FileObj } isDir(): this is DirObj { return this instanceof DirObj } } class FileObj extends FSObj { content: string = '' } class DirObj extends FSObj { children = [] } let a: FSObj = new FileObj() if (a.isFile()) { a.content } if (a.isDir()) { a.children }
2.8.7. 构造器参数属性
class Foo { constructor(private a: number = 1, public readonly b: number[] = []) } const f = new Foo() f.b
2.8.8. 类表达式
let Some = class <T>{ constructor(public content: T) { } } let a = new Some('') a.content
2.8.9. 抽象类
abstract class Base { abstract getName(): string printName() { console.log('welcome', this.getName()) } } class Derived extends Base { getName() { return "Mary" } } function greet(ctor: new () => Base) { let c = new ctor() c.printName() } greet(Derived) greet(Base) // wrong
2.8.10. 类之间的关系
如果两个类,字段重合,那么可以认为是等同的
class A { name: string } class B { name: string } class C { name: string last: string } let a: A = new B() // right let c: A = new C() // right
3. Reference
3.1. 内置的工具类型
3.1.1. Awaited<T>
type A = Awaited<Promise<string>>; // string // 可以递归 type B = Awaited<Promise<Promise<string>>>; // string type C = Awaited<Promise<string> | number>; // string | number
3.1.2. Partial<T>
将属性都标识成 可选
interface Todo { title: string; desc: string; } type PT = Partial<Todo>; // { // title?: string | undefined; // desc?: string | undefined; // }
3.1.3. Required<T>
将所有属性标识成 必须
interface Todo { title: string; desc?: string; } type T = Required<Todo>; // { // title: string; // desc: string; // }
3.1.4. Readonly<T>
将所有属性标识成 只读
interface Todo { title: string; desc?: string; } type T = Readonly<Todo>; // { // readonly title: string; // readonly desc?: string | undefined; // } function freeze<T>(obj: T): Readonly<T>
3.1.5. Record<Keys, T>
根据 keys 构建一个类型, 其属性的类型为 T
interface CatInfo { name: string; age: number; } let cats: Record<'kitty' | 'boris', CatInfo> = { kitty: { name: 'kitt', age: 1 }, boris: { name: 'boris', age: 1 }, }; cats.boris.age;
3.1.6. Pick<T, Keys>
从 T 中选择 Keys 构成新的类型
interface Todo { title: string; desc: string; completed: boolean; } type TodoPreview = Pick<Todo, 'desc' | 'title'>; // { // desc: string; // title: string; // }
3.1.7. Omit<T, Keys>
从 T 中剔除 Keys 构成新的类型
interface Todo { title: string; desc: string; completed: boolean; } type TodoPreview = Omit<Todo, 'desc' | 'completed'>; // { // title: string; // }
3.1.8. Exclude<UnionType, ExcludeMembers>
从 UnionType 中 剔除 ExcludeMembers
type A = 'a' | 'b' | 'c' | 'd'; type B = Exclude<A, 'c' | 'd'>; // 'a' | 'b'
3.1.9. Extract<Type, Union>
从 Type 中提取 Union 构成新的类型
type A = 'a' | 'b' | 'c' | 'd'; type B = Extract<A, 'c' | 'd' | 'e'>; // 'c' | 'd'
3.1.10. NonNullable<T>
剔除 null undefined
type A = string | number | null | undefined; type B = NonNullable<A>; // string | number
3.1.11. Parameters<T>
将函数类型的参数提取成元组类型
type A = Parameters<(a: string, b: boolean) => void>; // [a: string, b: number] // 泛型 type B = Parameters<<T>(a: T) => T>; // [a: unknown] type C = Parameters<any>; // unknown[] declare function f1(arg: { a: number; b: string }): void; type D = Parameters<typeof f1>; // [arg: { // a: number; // b: string; // }]
3.1.12. ConstructorParameters<T>
与 Parameters<T> 类似, 但是 T 必须是构造器
type T0 = ConstructorParameters<ErrorConstructor>; // [message?: string | undefined]
3.1.13. ReturnType<T>
获取函数的返回类型
type T0 = ReturnType<any>; // any type T1 = ReturnType<<T>(a: T) => T>; // unknown type T2 = ReturnType<<T extends number[]>(a: T) => T>; // number[] declare function f1(): number; type T3 = ReturnType<typeof f1>; // number
3.1.14. InstanceType<T>
类的实例的类型
class C { a: string; b: number; } type C0 = InstanceType<typeof C>; let a: C0 = { a: ',', b: 1, };
3.1.15. ThisParameterType<T>
获取 this 的类型
declare function f(this: string): void; type T0 = ThisParameterType<typeof f>; // string
3.1.16. OmitThisParameter<T>
去除 this 的类型
declare function f(this: string): void; type f1 = OmitThisParameter<typeof f>; // ()=>void
3.1.17. ThisType<T>
没有产生新的类型, 但是可以给标记其他类型中的 this 的类型, 需要开启 noImplicitThis
type ObjectDescriptor<D, M> = { data?: D; methods?: M & ThisType<D & M>; // Type of 'this' in methods is D & M }; function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M { let data: object = desc.data || {}; let methods: object = desc.methods || {}; return { ...data, ...methods } as D & M; } let obj = makeObject({ data: { x: 0, y: 0 }, methods: { moveBy(dx: number, dy: number) { this.x += dx; // Strongly typed this this.y += dy; // Strongly typed this this.xxx // wrong }, }, }); obj.x = 10; obj.y = 20; obj.moveBy(5, 5);
3.1.18. Uppercase<StringType>
3.1.19. Lowercase<StringType>
3.1.20. Capitalize<StringType>
3.1.21. Uncapitalize<StringType>
3.2. Cheat Sheet
内置的基础类型: number string boolean null undefined symbol bigint any unknown void never