interface I3 {
a:string
}
interface I3{
b:number
}
const obj:I3 = {
a:'',
b:13,
}
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
// function foo(arg: T):T {
// return arg
// }
const foo = (arg: T) => {
return arg
}
foo("linbudu")
foo(1);
foo('sss');
foo(undefined);
foo({})
// 泛型约束
type People = {
key : 'people'
}
type a = People<'aaa'>; // {a:"aaa"}
条件类型extends ? : ,它和前面的泛型约束共用extends关键字但是确是完全不一样的东西,对于extend ? : 有一个更简单的说法就是三目运算符(条件运算符),其实也就是ts中的if else
type D = number extends number | string ? true : false //true
type D1 = number extends boolean | string ? true : false //false
// fasle
// 裸类型参数
type Naked = T extends boolean ? "Y" : "N";
// 非裸类型参数
type Wrapped = [T] extends [boolean] ? "Y" : "N";
// "N" | "Y"
type Distributed = Naked;
// "N"
type NotDistributed = Wrapped;
( A | B | C ) extends T ? X : Y
// 相当于
(A extends T ? X : Y) | (B extends T ? X : Y) | (B extends T ? X : Y)
// 使用[]包裹后,不会进行额外的分发逻辑。
[A | B | C] extends [T] ? X : Y
这是一种联合类型的拓展用法,当类型参数为联合类型,并且在条件类型左边直接引用该类型参数的时候,TypeScript 会把每一个元素单独传入来做类型运算,最后再合并成联合类型,这种语法叫做分布式条件类型
触发分布式条件类型三条件
//T中排除U
type MyExclude = T extends U ? never : T;
type R = MyExclude<'a' | 'b' | 'c', "a">
// 实现一个IsUnion判断A是否为联合类型
type IsUnion =
// 通过A extends A触发A的分布式类型
A extends A
// 这时的A已经是触发后的分布式类型,而B不是,
// B通过[B]来避免触发联合类型而[A]则是触发过后的
// 如果这时输入的是联合类型[B]肯定是不等于[A]的
? [B] extends [A]
? false
: true
: never
type Test = T extends true ? 1 : 2;
type A = IsUnion //true
//boolean 其实也是联合类型,所以会把true 和false 分别传入求值,最后结果合并成联合类型,所以是1 | 2
type A1 = IsUnion //true
type R1 = Test //1|2
type A2 = IsUnion //false
type R2 = Test //2
//any 不是联合类型,这里是因为条件类型对 any 做了特殊处理,如果左边是 any,那么直接把 trueType 和 falseType 合并成联合类型返回.
type A3 = IsUnion //false
type R3 = Test; //1|2
//never类型在进行条件判断的时候并不会遵循正常的逻辑,如果条件类型左边是类型参数,并且传入的是 never,那么直接返回 never
type A4 = IsUnion //never
type R4 = Test; //never
// 加了一个[]当成了一个元组和绕过触发联合类型的逻辑一样
type IsNever = [T] extends [never] ? true : false
type A5 = IsNever //true
type R5 = IsNever //false
参考文档:这几个 TypeScript 类型,90% 的人说不出原因 - 掘金
TypeScript 中的AnyRecord采用 形式Record
type User = {
name: string,
age: number
}
const myData:Record = {
"aa" : { name: "John", age:12},
"bb" : { name: "Sarah", age:14},
}
type countK = 'c' | 'd' | 'e'
const myData1:Record = {
"c" : { name: "John", age:12},
"d": { name: "Sarah", age: 14 },
"e" : { name: "Sarah", age:14},
}
构造一个所有属性都设置为可选的类型Type。该实用程序将返回一个表示给定类型的所有子集的类型。相当于?
type User = {
name: string,
age: number
}
const a: Partial = {
name:"技术"
}
interface A1 {
name: string;
age: number
}
function updateTodo(a: A1, fieldsToUpdate: Partial) {
return { ...a, ...fieldsToUpdate };
}
const a1 = {
name: "呦呦呦",
age: 14,
};
console.log('first', updateTodo(a1, {
name:'哈哈哈'
}))
//first { name: '哈哈哈', age: 14 }
console.log('first', updateTodo(a1, {
name: '哈哈哈',
age:166
}))
//first { name: '哈哈哈', age: 166 }
console.log('first', updateTodo(a1, {}))
//first { name: '呦呦呦', age: 14 }
我们有时需要强制对象具有必需的属性,即使原始类型将其中一些属性定义为可选的。为此,TypeScript 有一个名为Required
type User = {
name?: string,
age?: number
}
//类型 "{ name: string; }" 中缺少属性 "age",但类型 "Required" 中需要该属性
// const a: Required = {
// name:"技术"
// }
const a: Required = {
name: "技术",
age:12
}
构造一个所有属性都Type设置为 的类型readonly,这意味着无法重新分配构造类型的属性。
type User = {
name: string,
age: number
}
// type User = {
// readonly name: string,
// readonly age: number
// }
const a: Readonly = {
name: "技术",
age:12
}
//error 无法为“name”赋值,因为它是只读属性
a.name = '小小'
通过从 Type 中选择一组属性键(key)(字符串文字或字符串文字的并集)来构造一个类型。
// type Pick = {
// [P in K]: T[P];
// };
type User = {
name: string;
age: number;
hobby: string;
birthday: Date;
}
type User1 = 'hobby' | 'name'
//{ name: string;; hobby: string;}
const a: Pick = {
name: "",
hobby:''
}
通过从 Type 中选择所有属性然后删除键(key)(字符串文字或字符串文字的并集)来构造一个类型。
// type Pick = {
// [P in K]: T[P];
// };
type User = {
name: string;
age: number;
hobby: string;
is: boolean;
}
type User1 = 'hobby' | 'name'
const a: Omit = {
age: 12,
is: true
}
通过从 UnionType 中排除所有可分配给 ExcludedMembers 的联合成员来构造一个类型
// type Exclude = T extends U ? never : T;
type User = string | number | null
type User1 = Exclude
//number | null
const a: User1 = "2";
const a1: User1 = null;
通过从 Type 中提取可分配给 Union 的所有联合成员来构造一个类型。
type User = string | number | null
type User1 = Extract
//number
const a: User1 = 1;
通过排除null和undefined从构造一个类型Type。
type User = string | number | null | undefined;
//string | number
// type User1 = NonNullable
// 不能将类型“null”分配给类型“NonNullable"
// const a: NonNullable = null
const a: NonNullable = 1;
infer是 inference 的缩写,通常的使用方式是用于修饰作为类型参数的泛型,如: infer R,R表示 待推断的类型。通常 infer 不会被直接使用,而是与条件类型一起,被放置在底层工具类型中。如果说条件类型提供了延迟推断的能力,那么加上 infer 就是提供了基于条件进行延迟推断的能力
类型系统在获得足够的信息(通常来自于条件的延迟推断)后,就能将 infer 后跟随的类型参数推导出来,最后通常会返回这个推导结果。
/* 当有一个确定的类型在推导类型边上,ts会将先查找到需要提取类型中是否存在那个确定类型,
而后将剩下的再分别存储在确定类型的左边或者右边的推导类型中 */
type R11 = 'sssssuuuujjjj' extends `${infer F}${'uuuu'}${infer R}` ? R : never //sssss
type R22 = 'sssssuuuujjjj' extends `${infer F}${'jjj'}` ? F : never // "sssssuuuuj"
type R33 = 'sssssuuuujjjj' extends `${infer F}${'tti'}` ? F : never //never
//提取第一个数组元素
type R3 = T extends [infer F, ...infer R] ? F : never;
type A = R3<['a', 1, 2, 3, '4']> //'a'
//提取最后一个数组元素
type R4 = T extends [...infer F, infer R] ? R : never;
type A1 = R4<['a', 1, 2, 3, '4']> //'4'
// 提取字符串第一个元素 F第一个, S第二个 R剩余的
type FirstString = T extends `${infer F}${infer S}${infer R}` ? F : never;
type A2 = FirstString<'Sjdoissdo'> //'S'xw
// 将一个字符串 S 中的所有子字符串 From 替换为 To
type R5
= From extends '' ? S :
S extends `${infer First}${From}${infer Str}` ?
`${First}${To}${R5}` : S
type A5 = R5<"snooosikdookdood",'oo','KKKK'> //"snKKKKosikdKKKKkdKKKKd"
const foo = (): string => {
return "linbudu";
};
//返回函数值类型
// (...args: any[]) => infer R 是一个整体,这里函数的返回值类型的位置被 infer R 占据了。
// type R1 = T extends (...args: any[]) => infer R ? R : never
type R1 any> = T extends (...args: any) => infer R ? R : never
type A1 = R1
const a: A1 = 'ss';//string
// 函数传入第一个参数的类型
type R2 any> = T extends ( a:infer R,...args: any) => infer K ? R : never
type A2 = R2<(a: boolean, c: number, b: string) => void>
// 提取promise参数类型
type MyAwaited =
// 实现判断当前是否为promise且有输入值
T extends Promise ?
// 再判断当前输入的值是否还是一个promise是则递归调用函数
Key extends Promise ? MyAwaited : Key
: never
// 同事解法
type GetPromiseParamsType> = T extends Promise
? T extends Promise>
? GetPromiseParamsType
: Param
: never
/* chatGPT的解析
这是TypeScript中的一个类型别名,用于提取Promise T的解析类型并展开可能嵌套在T中的任何Promise类型。
如果T扩展了Promise,则意味着T是一个Promise类型,其解析类型为Param。类型别名返回Param。
如果T扩展了Promise,则意味着T是一个Promise类型,并且其解析类型也是一个Promise类型。在这种情况下,类型别名会递归地调用自身,使用内部Promise类型innerParam,直到解析类型不再是Promise类型。最后,它返回从最后一个Promise类型获得的解析类型。
如果T不扩展Promise,则意味着T不是一个Promise类型,因此类型别名返回never。 总之,这个类型别名对于获取可能在其内部具有嵌套Promise类型的Promise的解析类型非常有用。
*/
对象、class 在 TypeScript 对应的类型是索引类型(Index Type),我们如果要去循环遍历并且修改它就需要使用映射类型了
type D = T
interface I1 {
name: string;
age: number;
}
const a: I1 = {
name:"a",
age:12
}
const foo = (r: T,arr:K[]) => {
return arr.map(i => r[i])
}
console.log(foo(a,['age','name'])) //[12,'a]
/* 索引查询 keyof T 是查询索引类型中所有的索引,叫做索引查询。keyof T查询出来的结果是一个由T的索引组成的联合类型。
限制传入的第二个参数必须为T的索引
*/
type D = T;
/* 索引访问
T[Key] 是取索引类型某个索引的值,叫做索引访问,可以获取到某个确定索引的类型值,这和js中的差不多,都是通过T[key]这种方式获取到对象的数据。
*/
type F = T['a'];
type F1 = F<{ a: 'sss' }>; //'sss'
// 同样的我们也能通过key获取到一些基础数据,数组长度这种
type R = T['length'];
type A = R<[1, 2, 3, 4, 5]>; //5
/* 循环遍历
循环遍历通过in关键词,就和js中的for in for of遍历类型类似,它可以遍历联合类型的数据,而这三者结合起来则可以完成一个完整的遍历索引类型
*/
// 将T中索引为D的类型修改为number
type R1 = {
// keyof T得出了一个联合类型,通过in进行循环而key则是每一次循环的结果
// 而后通过判断D是否属于当前的key,而后返回结果
[k in keyof T]: k extends D?number:T[k]
}
type A1 = R1<{ a: '22', b: '4444' }, 'b'> //{a: '22';b: number;}
/* 条件循环
在映射类型中可以通过as关键字对其添加条件而限制返回
*/
type R2 = {
[k in keyof T as k extends K ? never: k] : T[k]
}
type A2 = R2<{ a: 'aa', b: 'bb' }, 'a'> //{ b: "bb";}
type R3 = {
readonly [key in keyof T] : T[key]
} & {
-readonly [key in keyof T as key extends K ? never : key] : T[key]
}
type A3= R3<{ a: 'aa', b: 'bb' }, 'a'> //{ b: "bb";}
/* {
readonly a: 'aa';
readonly b: 'bb';
} & {
b: "bb";
} */
/* 元组遍历
元组通过number进行循环 */
// 将元组转换为对象
type R4 = {
// 遍历元组, T[number]
[key in T[number]] : key
}
type A4 = R4<['3',4,5,'d']> //{5: 5;4: 4;3: "3";d: "d";}