接口
TS 中接口可以用于对类的一部分行为进行抽象,也常用于对对象的形状进行描述。 使用 interface 关键字创建,首字母必须大写。
- 可选属性:该属性可以不存在
- 任意属性:一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
- 只读属性:只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候
interface User {
name: string;
age?: number;
readonly isMale: boolean;
say: (words: string) => void;
phone: {
[name: string]: string;
};
}
const richard: User = {
name: 'Richard',
age: 20,
isMale: true,
say: (words) => {
console.log(words);
},
phone: {
移动: '1111111',
联通: '1111111',
电信: '1111111',
},
};
richard.age = 22;
// richard.isMale = true
// richard.name = undefined
richard.age = undefined;
richard.say('666');
// interface 继承
interface VIPUser extends User {
isVIP: boolean;
}
const superRichard: VIPUser = {
name: 'Richard',
age: 20,
isMale: true,
say: (words) => {
console.log(words);
},
phone: {
移动: '1111111',
联通: '1111111',
电信: '1111111',
},
isVIP: false,
};
类
TS 中除了实现 ES6 的类还对它进行了扩展
- public:修饰的属性或方法是公有的,可以在任何地方被访问,默认的所有的属性和方法都是 public 的
- private:修饰的属性和方法是私有的,不能在声明它的类的外部被访问
- protected:修饰的属性或方法是受保护的,它只能在类的内部和子类中被访问
class Car {
protected run() {
console.log('run');
}
// 类的内部
move() {
this.run();
}
}
class GTR extends Car {
init() {
// 子类
this.run();
}
}
const car = new Car();
const gtr = new GTR();
// car.run()
gtr.init();
// gtr.run()
- 抽象类: abstract 用于定义抽象类和其中的抽象方法,抽象类是不允许被实例化的。抽象类的抽象方法只能由子类实现。
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log('move');
}
}
// const animal = new Animal()
class Cat extends Animal {
makeSound(): void {
console.log('miao~~~');
}
}
const cat = new Cat();
cat.makeSound();
cat.move();
类与接口
- 类实现接口
一个类只能继承自另一个类,有时候不同的类之间可以有一些共同的特性,这时候就可以把特性提取成接口 用 implements 关键字来实现。一个类可以实现多个接口。
interface Alarm {
alert: () => void;
}
interface Light {
lightOn: () => void;
lightOff: () => void;
}
class Car implements Alarm, Light {
alert() {
console.log('Car alert');
}
lightOn() {
console.log('Car light on');
}
lightOff() {
console.log('Car light off');
}
}
const car = new Car();
car.alert();
car.lightOff();
car.lightOn();
- 接口继承接口:子接口中包含父接口的形状
- 接口继承类
class Point {
x: number = 0;
y: number = 0;
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = { x: 1, y: 2, z: 3 };
// 类作为接口的时候可以同时赋予实例的初始值
const point = new Point();
函数
- 函数声明
function sum(x: number, y: number): number {
return x + y;
}
sum(1, 3);
// 输入多余(或少于)要求的参数是不允许的
- 函数表达式
// sum的类型通过类型推论推断出来的
let sum = function (x: number, y: number): number {
return x + y;
};
// 显示定义
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
- 用 interface 定义函数的形状
interface SearchFunc {
(source: string, substring: string): boolean;
}
let mySearch: SearchFunc = function (source, substring) {
return source.search(substring) !== -1;
};
- 可选参数:可选参数后面不允许再出现必须参数
const add = (a: number, b?: number): number => a + b;
- 参数默认值:TS 会将添加了默认值的参数识别为可选参数,且它后边能跟必传参数
const add = (a: number, b: number = 0): number => a + b;
- 剩余参数:它只能是最后一个参数
function push(a: number, ...rest: number[]) {}
- 重载:允许一个函数接受不同数量或者类型的参数,解决多个可选参数时函数的调用方式
interface IDirection {
top: number;
right: number;
bottom: number;
left: number;
}
function assigned(all: number): IDirection;
function assigned(topAndBottom: number, leftAndRight: number): IDirection;
function assigned(top: number, right: number, bottom: number, left: number): IDirection;
function assigned(a: number, b?: number, c?: number, d?: any) {
if (b === undefined && c === undefined && d === undefined) {
b = c = d = a;
} else if (c === undefined && d === undefined) {
c = a;
d = b;
}
return {
top: a,
right: b,
bottom: c,
left: d,
};
}
assigned(1);
assigned(1, 2);
assigned(1, 2, 3, 4);
// 无此类型的调用方式
// assigned(1,2,3)
// assigned(1,2,undefined,3)
泛型
泛型是指在定义函数,接口或者类的时候不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
function createArray<T\>(length: number, value: T): Array<T\> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray<string\>(3, 'a');
// 或
createArray(3, 'a');
- 多个类型参数
function swap<T, U\>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
swap([7, 'seven']);
- 泛型约束:由于在使用泛型的时候事先不知道它是那种类型,所以不能随意操作它的属性和方法
function getArrayLength<T\>(arg: Array<T\>): Array<T\> {
console.log(arg.length);
return arg;
}
- 泛型接口
interface CreateArrayFunc {
<T\>(length: number, value: T): Array<T\>;
}
const createArrayFunc: CreateArrayFunc = function <T\>(length: number, value: T): Array<T\> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
};
createArrayFunc(3, 'x');
- 泛型类
class Stack<T\> {
private arr: T[] = [];
public push(item: T) {
this.arr.push(item);
}
public pop() {
this.arr.pop();
}
}
const stack = new Stack<string\>();
stack.push('string');
// stack.push(1) 报错
- 泛型参数的默认类型
function createArray<T = string\>(length: number, value: T): Array<T\> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
- 泛型约束
type Params = string | number;
class ParamsArray<T extends Params\> {
private arr: T[] = [];
public push(item: T) {
this.arr.push(item);
}
}
const paramsArray1 = new ParamsArray<string\>();
// const paramsArray2 = new ParamsArray<null\>() 报错
- 泛型约束与索引类型
function getValue<T extends object, U extends keyof T\>(obj: T, key: U) {
return obj[key];
}
getValue({ name: 'richard', id: 1 }, 'id');
- 使用多重类型进行泛型约束
interface FirstInterface {
doSomething(): number;
}
interface SecondInterface {
doSomethingElse(): string;
}
class Demo<T extends FirstInterface & SecondInterface\> {
// 注意,本示例是在非 「--strictPropertyInitialization」或者「--strict」下测试的
private genericProperty: T;
useT() {
this.genericProperty.doSomething();
this.genericProperty.doSomethingElse();
}
}
- 泛型与 new:传入一个构造函数返回它的实例
参数 type 的类型 {new(): T} 就表示此泛型 T 是可被构造的,在被实例化后的类型是泛型 T。
function factory<T\>(type: { new (): T }): T {
return new type();
}
function factory2<T\>(type: new () => T): T {
return new type();
}
类型断言
类型断言不是类型转换,断言成一个联合类型不存在的类型是不允许的
- 尖括号语法
function getLength(something: string | number): number {
if ((<string\>something).length) {
return (<string\>something).length;
} else {
return something.toString().length;
}
}
- as 语法
interface Person {
name: string;
age: number;
}
const person = {} as Person;
person.name = 'richard';
person.age = 20;
类型守卫
- instanceof
class Person {
name = 'richard';
age = 20;
}
class Animals {
name = 'pp';
color = 'red';
}
function getSomething(ags: Person | Animals) {
if (ags instanceof Person) {
console.log(ags.age);
}
}
- in
class Person {
name = 'richard';
age = 20;
}
class Animals {
name = 'pp';
color = 'red';
}
function getSomething(ags: Person | Animals) {
if ('color' in ags) {
console.log(ags.color);
}
}
- 字面量类型守卫
type Foo = {
kind: 'foo';
foo: number;
};
type Bar = {
kind: 'bar';
bar: number;
};
function doStuff(args: Foo | Bar) {
if (args.kind === 'foo') {
console.log(args.foo);
// console.log(args.bar) 报错
}
}
交叉类型
将多个类型使用 & 合并为一个类型
联合类型
联合类型使用
|
分割每个类型。当 TS 不能确定一个联合类型的变量到底是哪个类型的时候,我们只能发文此联合类型的所有类型里共有的属性或方法。
let id: string | number;
id = '1';
id = 1;
类型别名
使用 type 创建类型别名,常用在联合类型
type Type = string | number;
字符串字面量类型
常用来约束取值只能是某几个字符串中的一个
type EventNames = 'click' | 'scroll';
字面量类型和类型字面量
- 字面量类型:分为真值字面量、数字字面量、枚举字面量、大整数字面量、字符串字面量
const value1: 2 = 2;
const value2: 0b10 = 2;
const value3: 'richard' = 'richard';
const value4: false = false;
- 类型字面量
type FOO = {
baz: [number, 'richard'];
toString(): string;
bar: 1;
};
const value5: FOO = {
baz: [1, 'richard'],
toString: () => '',
bar: 1,
};
可辨识联合类型
// 假如现在有一个功能,在create的时候不需要id,delete的时候需要
interface Info {
username: string;
}
type UserAction =
| {
id: number;
action: 'delete';
info: Info;
}
| {
action: 'create';
info: Info;
};
const createAction: UserAction = {
action: 'create',
// id: 1, 报错
info: {
username: 'richard',
},
};
const deleteAction: UserAction = {
action: 'delete',
id: 1,
info: {
username: 'richard',
},
};
const UserReducer = (userAction: UserAction) => {
switch (userAction.action) {
case 'create':
console.log(userAction.info);
break;
case 'delete':
console.log(userAction.id);
break;
}
};
明确赋值断言
明确赋值断言是一项功能,它允许将!放置在实例属性和变量声明之后,来表明此属性已经确定它已经被赋值了
class Strict {
foo: number;
baz: boolean = true;
bar!: string;
constructor() {
this.foo = 1;
}
}
let initValue: number;
init();
console.log(initValue! + initValue!);
function init() {
initValue = 10;
}
is 关键字
function isString(test: any): test is string {
return typeof test === 'string';
}
function example(foo: number | string) {
if (isString(foo)) {
// 去掉 test is string 会报错
console.log(foo.length);
}
}
可调用类型注解
interface ToString {
new (): string;
}
declare const SomethingToString: ToString;
new SomethingToString();
索引类型: keyof
// 取出 obj 对象上的一些属性并返回这些属性的数组
function pick<T, K extends keyof T\>(obj: T, names: K[]): T[K][] {
return names.map((n) => obj[n]);
}
const user = {
username: 'richard',
id: 1,
};
const res = pick(user, ['id']);
映射类型: K in keys
interface IUser {
username: string;
id: number;
}
type partial<T\> = { [K in keyof T]?: T[K] };
type partialUser = partial<IUser\>;
const user1: partialUser = {
id: undefined,
};
条件类型: T extends U ? X : Y
T extends U ? X : Y 表示为若 T 能够赋值给 U,那么类型是 X,否则为 Y
- 条件类型的使用
declare function f<T extends boolean\>(x: T): T extends true ? string : number;
const x = f(Math.random() < 0.5);
const y = f(false);
const z = f(true);
infer 关键字
- 条件类型与 infer
// 如果 T 能赋值给 (param: infer P) => any,则结果是(param: infer P) => any类型中的参数 P,否则返回为 T,infer P表示待推断的函数参数.
type ParamType<T\> = T extends (param: infer P) => any ? P : T;
- infer 的应用
- 元组转联合类型
- 联合类型转交叉类型
type ElementOf<T\> = T extends Array<infer E\> ? E : never;
type TTuple = [string, number];
type ToUnion = ElementOf<TTuple\>; // string | number
type UnionToIntersection<U\> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void
? I
: never;
type Result = UnionToIntersection<string | number\>;
常用工具类型
内置工具类型
可查看 lib.es5.d.ts
keyof
typeof
+/-: 在映射类型中给属性添加修饰符
Partial<T>: 将属性全部变为可选属性(无法转换深层次的属性)
// 内部实现
type Partial<T\> = {
[P in keyof T]?: T[P];
};
// 示例
interface AccountInfo {
name: string;
email: string;
age: number;
}
const model: Partial<AccountInfo\> = {
name: 'richard',
};
- Required<T>: 将 T 中的所有属性转换为必选属性
// 内部实现
type Require<T\> = {
[P in keyof T]-?: T[P];
};
// 示例
interface AccountInfo {
name?: string;
email?: string;
age?: number;
}
const model: Require<AccountInfo\> = {
name: 'richard',
email: '',
age: 1,
};
- Readonly<T>: 将所有的属性标记为只读属性
// 内部实现
type Readonly<T\> = {
readonly [P in keyof T]: T[P];
};
// 示例
interface AccountInfo {
name: string;
email: string;
age: number;
}
const model: Readonly<AccountInfo\> = {
name: 'richard',
email: '',
age: 1,
};
// model.age = 20 报错
- Pick<T, K>: 从 T 中过滤出属性 K
// 内部实现
type Pick<T, K extends keyof T\> = {
[P in K]: T[P];
};
// 示例
interface AccountInfo {
name: string;
email: string;
age: number;
}
const model: Pick<AccountInfo, 'name' | 'age'\> = {
name: 'richard',
age: 1,
};
- Exclude<T, U>: 从 T 中排除那些可以赋值给 U 的类型
// 内部实现
type Exclude<T, U\> = T extends U ? never : T;
// 示例
type A = Exclude<'a' | 'b' | 'c' | 'd', 'b' | 'c' | 'e'\>; // 'a' | 'd'
const a: A = 'a';
const b: A = 'd';
- Extract<T, U>: 取 T,U 两者的交集属性
// 内部实现
type Extract<T, U\> = T extends U ? T : never;
// 示例
type A = Extract<'a' | 'b' | 'c' | 'd', 'b' | 'c' | 'e'\>; // 'b' | 'c'
- Omit<T, K>: 忽略 T 中的某些属性
// 内部实现
type Omit<T, K extends keyof any\> = Pick<T, Exclude<keyof T, K\>\>;
// 示例
interface AccountInfo {
name: string;
email: string;
age: number;
}
const account: Omit<AccountInfo, 'name' | 'age'\> = {
email: '',
};
- Record<K, T>: 用一组属性 K(类型 T)构造一个类型
// 内部实现
type Record<K extends keyof any, T\> = {
[P in K]: T;
};
// 示例
type Car = 'Audi' | 'BMW' | 'Benz';
type CarList = Record<Car, { age: number }\>;
const cars: CarList = {
Audi: { age: 119 },
BMW: { age: 113 },
Benz: { age: 133 },
};
- NonNullable<T>: 排除 T 的 null 和 undefined 属性
// 内部实现
type NonNullable<T\> = T extends null | undefined ? never : T;
// 示例
const value: NonNullable<number | null | undefined\> = 1;
- Parameters<T>: 获取一个函数的所有参数类型
// 内部实现
type Parameters<T extends (...args: any) => any\> = T extends (...args: infer P) => any ? P : never;
// 示例
interface IFun {
(name: string, age: number): boolean;
}
type params = Parameters<IFun\>;
const value1: params[0] = 's';
const value2: params[1] = 1;
- ConstructorParameters<T>: 获取类的构造函数参数类型
// 内部实现
type ConstructorParameters<T extends new (...args: any) => any\> = T extends new (
...args: infer P
) => any
? P
: never;
// 示例
class PersonDemo {
constructor(name: string, age: number) {
console.log(name);
console.log(age);
}
}
type Person = ConstructorParameters<typeof PersonDemo\>;
const v123: Person = ['', 1];
- ReturnType<T>: 获取 T 的返回类型
// 内部实现
type ReturnType<T extends (...args: any) => any\> = T extends (...args: any) => infer R ? R : any;
// 示例
type FunctionReturn = () => string;
const value: ReturnType<FunctionReturn\> = '';
- InstanceType<T>: 获取构造函数函数类型的返回类型
// 内部实现
type InstanceType<T extends new (...args: any) => any\> = T extends new (...args: any) => infer R
? R
: any;
// 示例
class C {
x = 0;
y = 0;
}
type T0 = InstanceType<typeof C\>; // C
type T1 = InstanceType<any\>; // any
type T2 = InstanceType<never\>; // any
type T3 = InstanceType<string\>; // Error
type T4 = InstanceType<Function\>; // Error
自定义常用类型
- 更改类型上的属性
使用 typescript 有时候需要重写一个库提供的 interface 的某个属性,但是重写 interface 有可能会导致冲突。
interface Test {
name: string;
say(word: string): string;
}
type Weaken<T, K extends keyof T\> = {
[P in keyof T]: P extends K ? any : T[P];
};
interface Test2 extends Weaken<Test, 'name'\> {
name: Test['name'] | number;
}
- 数组/元组 转联合类型
const ALL_SUITS = ['hearts', 'diamonds', 'spades', 'clubs'] as const; // TS 3.4
type SuitTuple = typeof ALL_SUITS; // readonly ['hearts', 'diamonds', 'spades', 'clubs']
type Suit = SuitTuple[number]; // union type : 'hearts' | 'diamonds' | 'spades' | 'clubs'
- 枚举 转联合类型
enum Weekday {
Mon = 1,
Tue = 2,
Wed = 3,
}
type WeekdayName = keyof typeof Weekday; // 'Mon' | 'Tue' | 'Wed'
- PartialRecord
interface Model {
name: string;
email: string;
id: number;
age: number;
}
interface Validator {
required: boolean;
trigger?: 'blur';
message?: string;
}
type PartialRecord<K extends keyof any, T\> = Partial<Record<K, T\>\>;
const validateRules: PartialRecord<keyof Model, Validator\> = {
name: { required: true, trigger: `blur` },
};
- Unpacked: 抽离关键类型
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\>; // string
type T1 = Unpacked<string[]\>; // string
type T2 = Unpacked<() => string\>; // string
type T3 = Unpacked<Promise<string\>\>; // string
type T4 = Unpacked<Promise<string\>[]\>; // Promise<string\>
type T5 = Unpacked<Unpacked<Promise<string\>[]\>\>; // string
工具类型设计
- 将深层次的属性变为可选
type DeepPartial<T\> = {
[U in keyof T]?: T[U] extends object ? DeepPartial<T[U]\> : T[U];
};
声明文件
在使用第三方非 TS 编写的库的时候,我们需要使用它的声明文件才能获得对应的代码补全,提示功能。
使用 @types/XXX
安装对应的声明文件
编写 d.ts 文件
declare var/let/const
声明全局变量declare function
声明全局方法declare class
声明全局类declare enum
声明全局枚举declare namespace
声明含有子属性的全局对象interface
和type
声明全局类型
declare const JQuery: (selector: string) => any;
declare function JQuery(selector: string): any;
declare class Person {
name: string;
say(): string;
constructor(name: string);
}
declare enum Directions {
Up,
Down,
Left,
Right,
}
declare namespace jQuery {
function ajax(url: string, settings?: any): void;
}
interface AjaxSettings {
method?: 'GET' | 'POST';
data?: any;
}
declare namespace jQuery {
function ajax(url: string, settings?: AjaxSettings): void;
}
声明合并
如果定义了两个相同的函数、接口或类,那他们会合并成一个类型。