TS
Last Edited Time
Feb 12, 2022 09:52 AM
date
Jul 16, 2021
slug
typescript
status
Published
tags
Typescript
必读系列
个人笔记
summary
TS 个人总结笔记
type
Post
目录
名词解释高级类型type guards(类型守护) 和区分类型type 和 interfaceEnum多态的 this 类型索引类型(Index types)和 index signatures(字符串索引签名)Mapped types 映射类型Conditional Types(有条件类型)Distribute conditional types(分布有条件类型)Type inference in conditional type(有条件类型的类型推断)特殊的类型ThisTypeDeclaration Merging(声明合并)interface 声明合并namespace.d.ts 的加载规则全局作用域和模块作用域类型区分特点区分Reference
名词解释
- Ambient simply means “without implementation”.
高级类型
type guards(类型守护) 和区分类型
- as 关键字: pet as Fish
- (T: any) => T is string: 用户自定义的断言方法
- in 关键字: “swim” in pet
- typeof 关键字: typeof x === “number”
- instanceof 关键字: padder instanceof SpaceRepeatingPadder
type 和 interface
- type 可以重命名 primitives(原始类型), unions(联合类型), tuples(类型数组)
- type 并不创建新类型, 只是给类型取了个新名字
- interface 可以拓展并合并同名类型, type 则会报错
Enum
- 数字枚举在不指定初始化值时默认从 0 取值
- 如果上一个 key 的值为
number
, 则下一个 key 的默认值为number + 1
- 如果上一个 key 的值不为
number
, 则下一个 key 一定要有initializer
- 枚举成员的值除了是常量,还可以是计算出来的结果
- 只有数字枚举类型可以用计算值
- 数字枚举值可以反向映射,字符串枚举值不行
- 枚举 key 不能是计算属性
- 枚举前面加个 const 可以减少开销
- 外部枚举 declare
多态的 this 类型
在类中可以使用 this 多态类型, 能很容易的表现连贯接口间的继承, 例如:
class ScientificCalculator extends BasicCalculator {
public constructor(value = 0) {
super(value);
}
public sin() {
this.value = Math.sin(this.value);
return this;
}
// ... other operations go here ...
}
let v = new ScientificCalculator(2)
.multiply(5)
.sin()
// 这里 this 类型继承了 add()
.add(1)
.currentValue();
索引类型(Index types)和 index signatures(字符串索引签名)
其中 key 的类型可以为:
number
string
symbol
可以使用
keyof
和 T[K]
来操作 index signature
, 又因为 string 类型的 key
有可能被 number 类型的 key
所引用到, 所以:interface A {
[key: string]: any;
}
type B = keyof A;
// type B = string | number
Mapped types 映射类型
由一个类型生成新类型的过程叫映射类型, 常用的关键字为:
keyof
: 获取类型的key
可选类型
in
: 和key
一起使用, 代表属于某联合类型
extends
: 这里的继承类似于断言,A extends B
即A
属于B
never
: 可以进行类型判断
infer
: 类型判断占位类型
&
: 可能与索引类型共用来完成通用+指定类型配置
?
: 移除参数的可选配置
new
: new (…args: any) => any 代表构造函数
// 常用的映射类型如下
type Readonly2<T> = {
readonly [P in keyof T]: T[P];
};
type Partial2<T> = {
[P in keyof T]?: T[P];
};
type Nullable2<T> = {
[P in keyof T]: T[P] | null;
};
type Pick2<T, P extends keyof T> = {
[key in P]: T[P];
};
type Record2<T extends string | number | symbol, V> = {
[key in T]: V;
};
type Required2<T> = {
[P in keyof T]-?: T[P];
};
type TypeName<T> = T extends string
? 'string'
: T extends number
? 'number'
: T extends boolean
? 'boolean'
: T extends undefined
? 'undefined'
: T extends Function
? 'function'
: 'object';
type Diff<T, U> = T extends U ? never : T;
type Filter<T, U> = T extends U ? T : never;
type Exclude2<T, K> = T extends K ? never : K;
type Omit2<T, P> = Pick<T, Exclude<keyof T, P>>;
type InstanceType2<T extends new (...args: any) => any> = T extends new (
...args: any
) => infer R
? R
: any;
type ConstructorParameters2<
T extends new (...args: any) => any
> = T extends new (...args: infer R) => any ? R : never;
// https://github.com/type-challenges/type-challenges/blob/master/questions/296-medium-permutation/README.md
type Permutation<T, K = T> = [T] extends [never]
? []
: K extends any
? [K, ...Permutation<Exclude<T, K>>]
: never;
Conditional Types(有条件类型)
T extends U ? X : Y
为有条件的类型, 如果没有充足的信息的话, 类型可能为 X | Y
Distribute conditional types(分布有条件类型)
在映射条件类型时, 会分别映射每个条件的类型,例如:
T extends U ? X : Y
在 T = A | B | C
时的结果为 (A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)
type T6 = TypeName<string | string[] | undefined>;
// ^ = type T6 = "string" | "undefined" | "object"
Type inference in conditional type(有条件类型的类型推断)
可以有多重类型判断
type Unpacked<T> = T extends (infer U)[]
? U
: T extends (...args: any[]) => infer U
? U
: T extends Promise<infer U>
? U
: T;
type T0 = Unpacked<string>;
// ^ = type T0 = string
type T1 = Unpacked<string[]>;
// ^ = type T1 = string
type T2 = Unpacked<() => string>;
// ^ = type T2 = string
type T3 = Unpacked<Promise<string>>;
// ^ = type T3 = string
type T4 = Unpacked<Promise<string>[]>;
// ^ = type T4 = Promise
type T5 = Unpacked<Unpacked<Promise<string>[]>>;
// ^ = type T5 = string
多重类型判断有可能会合并类型
type Foo<T> = T extends { a: infer U; b: infer U } ? U : never;
type T1 = Foo<{ a: string; b: string }>;
// ^ = type T1 = string
type T2 = Foo<{ a: string; b: number }>;
// ^ = type T2 = string | number
特殊的类型
ThisType
ThisType
一般会和 &
共用, ThisType
只会指定 this
的类型// 没有ThisType情况下
const foo = {
bar() {
console.log(this.a); // error,在foo中只有bar一个函数,不存在a
},
};
// 使用ThisType
const foo: { bar: any } & ThisType<{ a: number }> = {
bar() {
console.log(this.bar); // error,因为没有在ThisType中定义
console.log(this.a); // ok
},
};
foo.bar; // ok
foo.a; // error,在外面的话,就跟ThisType没有关系了
Declaration Merging(声明合并)
interface 声明合并
- 非方法的属性合并时必须不重复或有相同的类型
- 方法属性在合并的时候, 后加载的会有更高的优先级, 但是 single string literal type (e.g. not a union of string literals) 会被冒泡到顶部, 优先级更高
namespace
- 全局作用域下, 不可以定义值属性, 而且所有的类型属性都会被暴露, 不需要显示写 export
- 模块作用域下, 可以定义值和类型, 只有 export 的属性才可以被外部访问到; 可以合并同名的 namespace 以及里面的 interface 等, 但是不能共享不同 namespace 下的值
namespace 可以和 class, function 和 enum 合并:
// namespace 和 class 合并
class Logger {
public logLevel = Logger.LogLevel.Info;
}
namespace Logger {
export enum LogLevel {
Verbose,
Info,
}
}
// namespace 和 function 合并
function buildLabel(name: string): string {
return buildLabel.prefix + name + buildLabel.suffix;
}
namespace buildLabel {
export let suffix = '';
export let prefix = 'Hello, ';
}
// namespace 和 enum 合并
enum Color {
red = 1,
green = 2,
blue = 4,
}
namespace Color {
export function mixColor(colorName: string) {
if (colorName == 'yellow') {
return Color.red + Color.green;
} else if (colorName == 'white') {
return Color.red + Color.green + Color.blue;
} else if (colorName == 'magenta') {
return Color.red + Color.blue;
} else if (colorName == 'cyan') {
return Color.green + Color.blue;
}
}
}
.d.ts 的加载规则
- 和 nodejs 模块加载类似
- 除了加载同级同名的 *.d.ts 文件, 还会依次加载当前文件夹的所有的 纯 *.d.ts 文件
- 如果在 *.d.ts 中使用了 export, 则和 *.ts 加载逻辑类似
全局作用域和模块作用域
类型区分
- 类型全局作用域: *.d.ts 如果没有 import/export, 并且没有同名的 *.ts/*.js 文件
- 类型的类似 ts 模块作用域: *.d.ts 文件包括 import/export, 不能使用 /// <reference 引入
- 类型的模块作用域: *.d.ts 如果有同名的 *.ts/*.js 文件, 可以使用 /// <reference 引入, 但是拿不到全局类型
- ts 的全局作用域: *.ts 文件不包括 import/export
- ts 的模块作用域: *.ts 文件包括 import/export, 本地类型可以覆盖全局类型, 并不会与全局类型冲突
特点区分
- 是否可以被 import 或 export
- 是否可以使用全局类型
- 类型是否为全局类型
- 本地类型是否可以覆盖全局类型
- 是否可以被 /// <reference 引入