类型推断

ts 能根据一些简单的规则推断变量的类型。

从右向左

变量的类型可以由定义推断,类型从右向左流动。

  1. let x = 'jack'; //x 推断是 'string' 类型
  2. let y = 18; //y 推断是 'number' 类型
  3. x = y; //error,不能将 'string' 赋值给 'number'

底部留出

通过 return 关键字推断返回值的类型。

  1. function sum(x:number, y:number){
  2. return x + y;
  3. }
  4. let z = sum(1, 2); //z 推断是 'number' 类型

从左向右

函数参数、返回值类型也能通过赋值来推断。

  1. type Sum = (x:number, y:number) => number;
  2. let sum:Sum = (x,y) => {
  3. // x = ''; //error, x、y 推断是 'number' 类型
  4. return x + y; //返回值 推断是 'number' 类型
  5. }
  6. let z = sum(1, 2);

结构化

推断规则也适用于结构化的存在(对象字面量)。

  1. let person = {
  2. name: 'jack',
  3. age: 18,
  4. }
  5. let name = person.name;
  6. let age = person.age;
  7. age = 'a'; //error,不能把 “string” 类型赋值给 “number” 类型

解构

推断规则也适用于解构

  1. let person = {
  2. name: 'jack',
  3. age: 18,
  4. }
  5. let {name, age} = person;
  6. age = 'a'; //error,不能把 “string” 类型赋值给 “number” 类型
  7. let arr = [1,2,3];
  8. arr[0] = ''; //error,不能把 “string” 类型赋值给 “number” 类型

DefaultProps
  1. interface DefaultProps {
  2. name?:string;
  3. age?:number;
  4. }
  5. let defaultProps: DefaultProps = {
  6. // name: 'jack', //即使注释,也不影响defaultProps变量的类型,自然不会影响下边 Props类型。
  7. age: 18,
  8. }
  9. let props = {
  10. ...defaultProps,
  11. home: '北京',
  12. }
  13. type Props = typeof props;
  14. /* 等同于
  15. type Props = {
  16. home: string,
  17. name?: string|undefined,
  18. age?: number|undefined,
  19. }
  20. */

小心使用返回值

尽管 ts 一般情况下能推断函数的返回值,但是它可能并不是你想要的。

  1. function addOne(a:any){
  2. return a + 1;
  3. }
  4. function sum(a:number, b:number){
  5. return a + addOne(b);
  6. }
  7. let k = sum(1, 2); //k推断 是 any类型

交叉类型

https://www.yuque.com/zhuchaoyang/uugnl6/xa2glt#QBAES

typeof

获取一个变量的类型

  1. // 先定义类型,再定义变量
  2. type Person = {
  3. name:string;
  4. }
  5. let p1:Person = {name: 'jack'}
  6. // 先定义变量,再定义类型
  7. let x = {name: 'jack'}
  8. type X = typeof x;
  9. let x1:X = {name: 'lili'};

索引访问操作符

可以通过 [] 获取一个类型的子类型

  1. interface Person {
  2. name:string;
  3. age:number;
  4. job: {
  5. name:string;
  6. };
  7. interests: {
  8. name:string;
  9. level:number;
  10. }[];
  11. }
  12. let x:Person['job'] = {name: 'jack'};
  13. let y:Person['interests'][0]['level'] = 1;

keyof 索引类型查询操作符

  1. interface Person {
  2. name:string;
  3. age:number;
  4. gender: 'male' | 'female';
  5. }
  6. type PersonKey = keyof Person;
  7. // => type PersonKey1 = 'name'|'age'|'gender';
  8. function getValueBykey(p:Person, key:PersonKey){
  9. return p[key];
  10. }
  11. let val = getValueBykey({
  12. name: 'jack',
  13. age: 18,
  14. gender: 'male',
  15. }, 'name')
  16. console.log(val); //jack
  1. type keyOfAny = keyof any; //any的key组成的联合类型
  2. // => type keyOfAny = string | number | symbol;
  1. type Obj = {name: 'jack', age: 18};
  2. type ObjKeys = keyof Obj; //Obj的key组成的联合类型
  3. // => type ObjKeys = "name" | "age"
  4. type Objvalues = Obj[ObjKeys]; // Obj的value组成的联合类型
  5. // => type Objvalues = "name" | 18

映射类型

在定义的时候用 in 操作符去批量定义类型中的属性。

  1. interface Person {
  2. name:string;
  3. age:number;
  4. gender: 'male' | 'female';
  5. }
  6. // 批量把一个接口中的属性全部变成可选的(Partial 的源码就是这样实现的)
  7. type PartialPerson = {
  8. // 先通过 “keyof Person” 拿到 Person 所有key的集合;
  9. // 再用 in 遍历 所有的key
  10. // []表示任意属性
  11. [key in keyof Person]?: Person[key];
  12. }
  13. let p1:PartialPerson={};

通过key的数组获取值的数组

  1. //“K extends keyof T”是泛型K的约束条件,指“K”要满足是“keyof T”的子类型,否则就报错
  2. function pick<T, K extends keyof T>(o:T, names:Array<K>):Array<T[K]>{
  3. return names.map((n) => o[n]);
  4. }
  5. let user = {
  6. id: 1,
  7. name: 'jack',
  8. }
  9. type User = typeof user;
  10. type User2 = keyof User; // => type User2 = 'id' | 'name';
  11. let res = pick<User, keyof User>(user, ['id', 'name']);
  12. console.log(res); //[ 1, 'jack' ]

条件类型

在定义泛型的时候能够添加进逻辑分支,以后泛型更加灵活。

定义条件类型

  1. interface Fish {name1:string}
  2. interface Bird {name3:string}
  3. interface Water {name2:string}
  4. interface Sky {name4:string}
  5. // 如果 T 是 Fish 的子类型,那么类型是Water,否则是Sky
  6. type Condition<T> = T extends Fish ? Water : Sky;
  7. let c1:Condition<Fish> = {name2: 'jack'}; //传Fish,c1的类型就是 Water
  8. let c2:Condition<Bird> = {name4: 'jack'}; //传Bird,c2的类型就是 Sky

条件类型的分发

如果你传入的是一个联合类型,它会进行条件的分发。

  1. // 只有裸的类型(naked type)才可以分发,依次代入,最后取结果的联合类型
  2. type Condition<T> = T extends Fish ? Water : Sky;
  3. // 先用 Fish 代入泛型T,再用 Bird 代入泛型T,最后取结果的联合类型。
  4. // (Fish extends Fish ? Water : Sky) | (Bird extends Fish ? Water : Sky)
  5. // => Condition<Fish|Bird> = Water|Sky
  6. let c3:Condition<Fish|Bird> = {name2: 'jack'} //ok
  7. let c4:Condition<Fish|Bird> = {name4: 'jack'} //ok
  8. let c5:Condition<Fish|Bird> = {name2: 'jack', name4: 'jack'} //ok

条件类型有一个特性,就是【分布式有条件类型】,但是分布式有条件类型是有前提的,即条件类型里待检查的类型必须是 naked type parameter

  1. // T[] [T] {t: T} 等都不是裸的类型,只能整体代入。
  2. type Condition<T> = T[] extends Fish[] ? Water : Sky;
  3. // (Fish|Bird)[] 不是 Fish[] 的子类型,所以结果是 Sky 类型
  4. // => Condition<Fish|Bird> = Sky
  5. let con1:Condition<Fish|Bird> = {name2: 'jack'}; //error
  6. let con2:Condition<Fish|Bird> = {name4: 'jack'}; //ok

示例1:如果用户传递了name,就必须传递age
  1. interface Person {name:string, age:number}
  2. interface Person2 {age?:number, sex:string}
  3. type Pe<T> = T extends {name:string} ? Person : Person2;
  4. let p1:Pe<Person> = {name: 'jack', age: 18};
  5. let p2:Pe<Person2> = {age:18, sex: 'male'};

示例2:找出T中不包含U的部分
  1. // 找出T中不包含U的部分
  2. type Diff<T, U> = T extends U ? never : T;
  3. type R1 = Diff<'a'|'b'|'c', 'a'|'b'>;
  4. // => 'a' 是否是 'a'|'b' 的子类型,是 => never
  5. // => 'b' 是否是 'a'|'b' 的子类型,是 => never
  6. // => 'c' 是否是 'a'|'b' 的子类型,否 => 'c'
  7. // => type R1 = never | never | 'c';
  8. // => type R1 = 'c';
  9. let r1:R1 = 'c'; //ok
  10. // 找出T中包含U的部分
  11. type Filter<T, U> = T extends U ? T : never;
  12. type R2 = Filter<'a'|'b'|'c', 'a'|'b'>;
  13. // => type R2 = 'a' | 'b';
  14. let r2:R2 = 'a'; //ok
  15. let r3:R2 = 'b'; //ok

内置条件类型

Exclude 排除

从 T 可分配给的类型中排除 属于U子类型的类型

  1. // type MyExclude<T, U> = Exclude<T, U>; //内置类型方法
  2. type MyExclude<T, U> = T extends U ? never : T;
  3. type R = MyExclude<string|number|boolean, boolean>;
  4. // => string 不是 U(boolean) 的子类型,返回 string
  5. // => number 不是 U 的子类型,返回 number
  6. // => boolean 是 U 的子类型,返回 never
  7. // => R = string | number | never
  8. // => R = string | number

Extract 提取

从 T 可分配给的类型中提取 属于U子类型的类型

  1. // type MyExtract<T, U> = Extract<T, U>; //内置类型方法
  2. type MyExtract<T, U> = T extends U ? T : never;
  3. type R = MyExtract<string|number|boolean, string|number>;
  4. // => string 是 U(string|number) 的子类型,返回 string
  5. // => number 是 U 的子类型,返回 number
  6. // => boolean 不是 U 的子类型,返回 never
  7. // => R = string | number

NonNullable 排除null、undefined

从 T 中排除 null 和 undefined

  1. // type MyNonNullable<T> = NonNullable<T>; //内置类型方法
  2. type MyNonNullable<T> = T extends (null | undefined) ? never : T;
  3. type R = MyNonNullable<string|number|null|undefined>
  4. // => R = string | number

ReturnType 推断函数返回值类型

  • infer最早出现在此 PR 中,表示在 extends 条件语句中待推断的类型变量。
  • infer 的意思是声明一个 待推断 的类型变量R,R根据 infer 所在的位置推断类型
  • 仅条件类型的 “extends”子句中才允许 “infer”声明。
  • 获取函数类型的返回值类型。 ```typescript function getUser(){ return {name: ‘jack’, age: 10}; } type R1 = ReturnType; //内置类型方法

// T 要满足是一个函数类型,所以为 泛型T 也加上约束条件: T extends (…args:any[]) => any // infer 仅条件类型的 extends 子句中才允许 infer 声明,否则无法使用 // 这里 infer 处于函数的返回位置,R 就指 T返回值 的类型 type Fn = (…args:any[]) => any; type MyReturnType = T extends (…args:any[]) => infer R ? R : any;

type R2 = MyReturnType; // => R2 = {name:string, age:number}

  1. <a name="oMmNa"></a>
  2. ## Parameters 推断函数参数类型
  3. - Constructs a tuple type of the types of the parameters of a function type T
  4. - 以元祖形式返回
  5. - [Parameters](http://www.typescriptlang.org/docs/handbook/utility-types.html#parameterst)
  6. ```typescript
  7. type Fn = (...args:any[]) => any;
  8. type MyParameters<T extends Fn> = T extends (...args: infer P) => any ? P : never;
  9. type R2 = MyParameters<typeof getUser>;
  10. // => R2 = [x:string, y:number]

InstanceType、ConstructorParameters

  • InstanceType 获取 构造函数 new实例函数 的返回值类型(即实例类型)
  • ConstructorParameters 获取构造函数 new实例函数 的参数类型(即构造函数的参数类型)
  • InstanceType ```typescript class Person { constructor(name:string){} } type R1 = ConstructorParameters; //内置类型方法 type R3 = InstanceType; //内置类型方法

type Con = new (…args:any[]) => any; //声明 构造函数 类型

type MyConstructorParameters = T extends new (…args:infer P) => any ? P : never; type MyInstanceType = T extends new (…args:any[]) => infer R ? R : never;

type R2 = MyConstructorParameters; // => R2 = [name:string] type R4 = MyInstanceType; // => R4 = Person

  1. <a name="iTASL"></a>
  2. ## infer + 分布式
  3. - `infer` 关键字就是声明一个类型变量,当类型系统给足条件的时候类型就会被推断出来
  4. - [distributive-conditional-types](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types)
  5. - 「Distributive conditional types」主要用于拆分 extends 左边部分的联合类型
  6. - 「Distributive conditional types」是由「naked type parameter」构成的条件类型。而「naked type parameter」表示没有被 Wrapped 的类型(如:Array、[T]、Promise 等都是不是「naked type parameter」)。「Distributive conditional types」主要用于拆分 extends 左边部分的联合类型,举个例子:在条件类型 T extends U ? X : Y 中,当 T 是 A | B 时,会拆分成 A extends U ? X : Y | B extends U ? X : Y;
  7. - 利用在逆变位置上,同一类型变量的多个候选类型将会被推断为[交叉类型的特性](https://github.com/Microsoft/TypeScript/pull/21496)
  8. ```typescript
  9. // 获取一个对象中 name 属性的类型
  10. // “T” 是否是 “{name: infer X}” 的子类型。是,返回类型X;否,返回类型never
  11. type A<T> = T extends {name: infer X} ? X : never;
  12. // infer 意思是声明一个 待推断 的类型变量X,X根据infer所在的位置推断类型。
  13. // 这里 infer 处于对象name属性位置,X就指对象name属性的类型。
  14. let obj = {name: 'jack'};
  15. type Aname1 = A<typeof obj>;
  16. // => type Aname1 = string;
  17. type Aname2 = A<{name: number}>;
  18. // => type Aname2 = number;
  19. type Aname3 = A<string>;
  20. // => Aname3 = never;
  21. // 为泛型 T 添加约束,如果不是 "{[key:string]:any}" 的子类型,就报错
  22. type B<T extends {[key:string]:any}> = T extends {name: infer X} ? X : never;
  23. type Bname1 = B<string>; //error,类型 “string” 不满足约束 “{[key:string]:any}”
  24. type Bname2 = B<{}>; //ok

tuple 转 union
  1. type ElementOf<T> = T extends Array<infer E> ? E : never;
  2. type Ttuple = [string, number];
  3. type TupleToUnion = ElementOf<Ttuple>;
  4. // => type TupleToUnion = string|number

联合类型 转 交叉类型
  1. //union 转 intersection 的操作多用于 mixin 中
  2. //https://github.com/Microsoft/TypeScript/issues/27907
  3. // string|number => string & number
  4. type T1 = {name:string};
  5. type T2 = {age:number};
  6. type ToIntersection<T> = T extends {
  7. a: (x:infer U) => void,
  8. b: (x:infer U) => void,
  9. } ? U : never;
  10. type T3 = ToIntersection<{
  11. a: (x:T1) => void,
  12. b: (x:T2) => void,
  13. }>
  14. // => U 必须同时满足 是 T1、T2 的子类型(参数是逆变的)
  15. // => type T3 = T1 & T2;

内置工具类型

  • TS 中内置了一些工具类型来帮助我们更好地使用类型系统
  • utility-types
  • TypeScript中增加了对映射类型修饰符的控制
  • 具体而言,一个 readonly? 修饰符在一个映射类型里可以用前缀 +-来表示这个修饰符应该被添加或移除

Partial 可选

  • 将传入的属性由非可选变为可选。默认只是最外层变为可选。
  • 修饰符 +?? ,其中+号可以去掉。 ```typescript interface ICompany{name:string, address:string} interface IPerson{name:string, age:number, company:ICompany}

type R1 = Partial; //内置类型方法

// 先通过 “keyof T” 拿到 T 所有key的集合;再用 in 遍历 所有的key type MyPartial = { [K in keyof T]?: T[K] } type R2 = MyPartial; // => R2 = { // name?: string|undefined, // age?: number|undefined, // company?: ICompany|undefined, //单层可选 // }

// 但是上面的只是单层,怎么办呢? 类型深度递归 type MyDeepPartial = { [K in keyof T]?: T[K] extends object ? MyDeepPartial : T[K] } type R3 = MyDeepPartial;

  1. <a name="a6Tp9"></a>
  2. ## Required 必选
  3. - 将传入的属性中的可选项变为必选项,默认只是最外层变为必选。
  4. - 修饰符 `-?`
  5. ```typescript
  6. interface ICompany{name?:string, address?:string}
  7. interface IPerson{name?:string, age?:number, company?:ICompany}
  8. type R1 = Required<IPerson>; //内置类型方法
  9. type MyRequired<T> = {
  10. [K in keyof T]-?: T[K]
  11. }
  12. type R2 = MyRequired<IPerson>
  13. // => R2 = {
  14. // name:string,
  15. // age:number,
  16. // company:ICompany,
  17. // }

Readonly 只读

为传入的属性每一项都加上 readonly 修饰符来实现

  1. interface ICompany{name?:string, address?:string}
  2. interface IPerson{name?:string, age?:number, company?:ICompany}
  3. type R1 = Readonly<IPerson>; //内置类型方法
  4. type MyReadonly<T> = {
  5. readonly [K in keyof T]: T[K]
  6. }
  7. type R2 = MyReadonly<IPerson>
  8. // => R2 = {
  9. // readonly name?: string|undefined,
  10. // readonly age?: number|undefined,
  11. // readonly company?: ICompany|undefined,
  12. // }
  13. // 只把name属性变为只读
  14. type ReadonlyNamePerson = Person & {readonly name:string};

Pick 挑选属性

从对象属性中,提取指定的属性。Pick<object, union>

Pick 从对象里选属性; extract 从类型中选择类型

  1. interface IPerson{name:string, age:number, sex: string};
  2. type R1 = Pick<IPerson, 'name'|'age'>; //内置类型方法
  3. type MyPick<T, U extends keyof T> = {
  4. [K in U]: T[K]
  5. }
  6. type R2 = MyPick<IPerson, 'name'|'age'>
  7. // => R2 = {name:string, age:number}
  8. // 提取指定属性并组成新对象
  9. function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
  10. const result: any = {};
  11. keys.map(key => {
  12. result[key] = obj[key];
  13. });
  14. return result
  15. }
  16. let p3:IPerson = {name: 'jack', age: 10, sex: 'male'};
  17. let result:Pick<IPerson, 'name'|'age'>
  18. = pick<IPerson, 'name'|'age'>(p3, ['name', 'age']);
  19. console.log(result); //{ name: 'zhufeng', age: 10 }

Omit 忽略属性

Omit 从对象属性中,忽略指定属性,提取剩下的。Omit<object, union>

  1. interface IPerson{name:string, age:number, sex: string};
  2. type R1 = Omit<IPerson, 'name'|'age'>; //内置类型方法
  3. type MyOmit<T, U extends keyof T> = Pick<T, Exclude<keyof T, U>>
  4. type R2 = MyOmit<IPerson, 'name'|'age'>
  5. // => R2 = {sex:number}
  6. // 示例1:只想要里面的name属性变为必选
  7. interface IPerson2{name?:string, age?:number, sex: string};
  8. type R3 = Omit<IPerson2, 'name'> & {name:string};
  9. let r3:R3 = {name: 'jack', sex: 'male'}; //ok
  10. let r4:R3 = {sex: 'male'}; //error

Record 描述对象

  • 将一个类型的所有属性都映射到另一个类型上并创造一个新的类型。 ```typescript // 对象的属性名只能是字符串、数字、symbol。所以为 泛型K 添加约束 // keyof any 等同于 string | number | symbol type Record = {

// [] 表示任意属性 // 如果是示例1,等同于: // [key:string]: string; // [key:number]: string;

// 如果是示例2,等同于 // x: {value:number}, // y: {value:number} }; // 示例1: // k的key只能是string|number类型,k的value只能是string类型 let k:Record = {name: ‘jack’, age: ‘10’};

// 示例2: type Point = ‘x’ | ‘y’; type PointList = Record; let cars:PointList = { x: {value: 10}, y: {value: 20}, }

  1. <a name="HiG4J"></a>
  2. ##### 示例:把一个对象映射成新的对象
  3. ```typescript
  4. // 把一个对象映射成新的对象
  5. function mapObj<K extends keyof any, V, X>(
  6. obj: Record<K, V>, //老对象
  7. cb: (item:V, key:K) => X, //映射函数
  8. ):Record<K, X>{
  9. let result = {} as Record<K, X>;
  10. for (let key in obj){
  11. result[key] = cb(obj[key], key);
  12. }
  13. return result;
  14. }
  15. let obj = {a: 1, b: 2};
  16. let newObj = mapObj(obj, (item, key) => {
  17. return item*2;
  18. })
  19. console.log(newObj); //{a: 2, b: 4}

自定义高级类型

utility-types

Proxy 代理

  1. let data = {name: 'jack', age: 18};
  2. type Proxy<V> = {
  3. get():V,
  4. set(value:any):void,
  5. }
  6. type Proxify<T extends object> = {
  7. [K in keyof T]: Proxy<T[K]>
  8. }

设置代理(装包)
  1. function proxify<T extends object>(obj:T):Proxify<T>{
  2. let result = {} as Proxify<T>;
  3. for (let key in obj){
  4. let value = obj[key];
  5. Object.defineProperty(result, key, {
  6. enumerable: true,
  7. value: {
  8. get: () => value,
  9. set: (newValue:any) => {
  10. value = newValue;
  11. },
  12. },
  13. })
  14. }
  15. return result;
  16. }
  17. let proxifyData = proxify(data); //包装后的对象
  18. console.log(proxifyData); //{name: {get(){}, set(){}}, age: {get(){}, set(){}}}
  19. console.log(proxifyData.name.get()); //jack
  20. proxifyData.name.set('lucky');
  21. console.log(proxifyData.name.get()); //lucky

取消代理(拆包)
  1. function unProxify<T extends object>(obj: Proxify<T>):T{
  2. let result = {} as T;
  3. for (let key in obj){
  4. result[key] = obj[key].get();
  5. }
  6. return result;
  7. }
  8. let originData = unProxify(proxifyData); //源对象,即拆包后的对象
  9. console.log(originData); //{name: 'lucky', age: 18}

Diff 差集

从O1的属性中,提取二者不同的属性。Diff<O1, O2>

  1. interface Person1 {name:string, age:number, address:string};
  2. interface Person2 {address:string, sex:boolean};
  3. type Diff<T extends object, U extends object> = Omit<T, keyof U>;
  4. type R1 = Diff<Person1, Person2>;
  5. // => R1 = {name:string, age:number}

Inter 交集

从O1的属性中,提取二者相同的属性。Inter<O1, O2>

  1. interface Person1 {name:string, age:number, address:string};
  2. interface Person2 {address:string, sex:boolean};
  3. interface Person3 {address:boolean, sex:boolean};
  4. // 交叉类型,交叉后应是两者的子类型
  5. type R1 = Person1 & Person2; // => R1 = {name:string, age:number, address:string, sex:boolean}
  6. // 交叉属性,取两个对象相同的地方
  7. type Inter<T extends object, U extends object > = Pick<T, Extract<keyof T, keyof U>>;
  8. type Inter2<T extends object, U extends object> = Omit<T, Exclude<keyof T, keyof U>>;
  9. type R2 = Inter<Person1, Person2>; // => R2 = {address:string}
  10. type R3 = Inter2<Person1, Person2>; // => R3 = {address:string}
  11. // 如果同一属性,类型不同,取第一个参数的类型。
  12. type R4 = Inter<Person1, Person3>; // => R2 = {address:string}

Overwrite 覆盖

  • 针对O1,用O2的属性覆盖O1的属性。Overwrite(O1, O2)
  • mapped-types ```typescript interface Person1 {name:string, age:number, }; interface Person2 {age:string, address:string};

type Diff = Omit; type Inter = Pick>; type Overwrite = Diff & Inter

type R1 = Overwrite; // => R1 = {name:string, age:string} let r1:R1 = {name: ‘jack’, age: ‘xxx’}; //ok

  1. <a name="6lZHm"></a>
  2. ## Merge 合并
  3. 将两个对象合并,一般都是以后者为准。`merge<O1, O2>`
  4. ```typescript
  5. interface Person1 {name:string, age:number, };
  6. interface Person2 {age:string, address:string};
  7. // 展开类型,方便提示
  8. type Compute<T extends object> = T extends Function ? T : {
  9. [K in keyof T]: T[K]
  10. }
  11. type Merge<T extends object, U extends object> = Omit<T, keyof U> & U;
  12. type R1 = Merge<Person1, Person2>; //但是这样的提示不易读
  13. type R1Compute = Compute<R1>; //格式化后,鼠标移上去,就很已读了
  14. // => R1 = {name:string, age:string, address: string}
  15. let r1:R1 = {name: '', age: '', address: ''}; //ok

Mutable 移除只读

  • 将 T 的所有属性的 readonly 移除
    1. type Mutable<T> = {
    2. -readonly [P in keyof T]: T[P]
    3. }
    4. type A = {
    5. readonly name:string;
    6. readonly age:number;
    7. }
    8. type R = Mutable<A>;
    9. // => type R = {name:string; age:number}