泛型可以理解成一段类型逻辑,需要类型参数来表达。有了类型参数以后,可以在输入类型与输出类型之间,建立一一对应关系
- 函数
- 接口
- 类
- 类型别名type
函数的泛型写法,类型参数放在尖括号中,写在函数名后面
function id<T>(arg:T):T {
return arg;
}
变量形式定义的函数
let myId:<T>(arg:T) => T = id;
接口的泛型写法
interface Box<Type> {
contents: Type;
}
类的泛型写法
class Pair<K, V> {
key: 1. K;
value: V;
}
类型别名的泛型写法
type Container<T> = { value: T };
const a: Container = { value: 0 };
数组的泛型,Array是 TypeScript 原生的一个类型接口 数组的泛型表示
let arr:Array<number> = [1, 2, 3];
其他
Map<K, V>
Set<T>
Promise<T>
例子 map todo
function comp<Type>(a:Type, b:Type) {
if (a.length >= b.length) {
return a;
}
return b;
}
上面示例中,类型参数 Type 有一个隐藏的约束条件:它必须存在length属性。如果不满足这个条件,就会报错
function comp<T extends { length: number }>(
a: T,
b: T
) {
if (a.length >= b.length) {
return a;
}
return b;
}
comp([1, 2], [1, 2, 3]) // 正确
comp('ab', 'abc') // 正确
comp(1, 2) // 报错
1、尽量少用泛型。加大代码的复杂性,使其变得难读难写 2、类型参数越少越好。 3、类型参数需要出现两次。如果类型参数在定义后只出现一次,那么很可能是不必要的。 4、泛型可以嵌套 类型参数可以是另一个泛型 以上,是在满足实现条件的前提下
####防抖例子
function deb<T extends any[],R>(fn:(...args: T)=>R,time:number):(...args:T)=>void{ return ()=>{} }
function handle(a:number,b:string){ return [a,b] }
const f = deb(handle,200)
精确表达对象的属性类型 属性映射,即将一个类型的所有属性逐一映射成其他值 keyof 是一个单目运算符,接受一个对象类型作为参数,返回该对象的所有键名组成的联合类型
type MyObj = { foo: number, bar: string, };
type Keys = keyof MyObj; // 'foo'|'bar'
type KeyT = keyof any;
type Result = keyof ['a', 'b', 'c']; // 返回 number | "0" | "1" | "2" // | "length" | "pop" | "push" | ···
type U = 'a'|'b'|'c';
type Foo = { [Prop in U]: number; };
// 等同于
type Foo = { a: number, b: number, c: number };
判断+映射 映射的过程当中,可以通过添加+,-来添加.移除修饰符
有了 keyof 和 in ,可以封装高级内置工具
Readonly Required Partial deepPartial
type Person = {
age: number;
name: string;
alive: boolean;
};
// Age 的类型是 number
type Age = Person['age'];
// number|string
type T = Person['age'|'name'];
type LiteralTypeName<T> =
T extends undefined ? "undefined" :
T extends null ? "null" :
T extends boolean ? "boolean" :
T extends number ? "number" :
T extends bigint ? "bigint" :
T extends string ? "string" :
never;
// "string" | "number" | "boolean"
type Result2 = LiteralTypeName<true | 1 | 'a'>;
exclude extract
Omit 以一个类型为基础支持剔除某些属性,然后返回一个新类型,用联合类型传多值
Pick 从类型定义的属性中,选取指定一组属性,返回一个新的类型定义,用联合类型传多值
infer只能在条件类型extends ?字句中使用
type Wrong1<T extends (infer U)[]> = T[0] // Error
type Wrong2<T> = (infer U)[] extends T ? U : T // Error
type MyType<T> =
T extends {
a: infer M,
b: infer N
} ? [M, N] : never;
// 用法示例
type T = MyType<{ a: string; b: number }>;
// [string, number]
当我们处理复杂的对象类型时,可能只需要使用其中的一部分属性。通过索引类型和泛型,我们可以轻松提取对象的部分属性,避免手动定义新的接口或类型。
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
const picked: Partial<T> = {};
keys.forEach((key) => {
picked[key] = obj[key];
});
return picked as Pick<T, K>;
}
const user = {
id: 1,
name: "Alice",
age: 25,
email: "[email protected]",
};
const pickedUser = pick(user, ["name", "email"]);
pickedUser.
console.log(pickedUser);
// 输出 { name: 'Alice', email: '[email protected]' }
通过结合泛型约束和索引类型,我们可以从 user 对象中选择需要的属性,返回一个新的对象类型。
Record 泛类约束 1、Record定义的类型,我们传递的参数必须是一个对象 2、所传参数的属性,就是recode第一个参数定义的属性,而值则是第二个参数决定的 3、它传参数应和record定义的第一个参数包吃一致,(顺序无关)
其他
returntype Uppercase Lowercase Capitalize Uncapitalize Parameters NonNullable
https://github.com/type-challenges/type-challenges
StartsWith EndsWith
type a = StartsWith<'abc', 'ac'>
type b = StartsWith<'abc', 'ab'>
type c = EndsWith<'abc', 'abc'>
type d = EndsWith<'abc', 'abc'>
type IndexOf<T extends any[], U, R extends any[] = []> =
T extends [infer K, ...infer Rest]
? K extends U
? R['length']
: IndexOf<Rest, U, [...R, 1]>
: -1
type MergeValues<One, Other> =
One extends Other
? One
: Other extends unknown[]?
IndexOf<Other,One> extends -1?
[One, ...Other]
: Other
:[One, Other]
type MergeParams<
OneParam extends Record<string, any>,
OtherParam extends Record<string, any>
> = {
readonly [Key in keyof OneParam | keyof OtherParam]:
Key extends keyof OneParam
? Key extends keyof OtherParam
? MergeValues<OneParam[Key], OtherParam[Key]>
: OneParam[Key]
: Key extends keyof OtherParam
? OtherParam[Key]
: never
}
type ParseParam<Param extends string> =
Param extends `${infer Key}=${infer Value}`
? {
[p in Key]: Value
} : Param extends '' ? {} : { [K in Param]: true }
// Record<string, any>;
type ParseQueryString<Str extends string>
= Str extends `${infer Param}&${infer Rest}`
? MergeParams<ParseParam<Param>, ParseQueryString<Rest>>
: ParseParam<Str>;
function parseQueryString<T extends string>(queryStr:T): ParseQueryString<T> {
if (!queryStr || !queryStr.length) {
return {} as ParseQueryString<T>;
}
const queryObj = {};
const items = queryStr.split('&');
items.forEach((item) => {
const [key, value] = item.split('=');
if (queryObj[key]) {
if(Array.isArray(queryObj[key])) {
queryObj[key].push(value);
} else {
queryObj[key] = [queryObj[key], value]
}
} else {
queryObj[key] = value;
}
});
return queryObj as any;
}
const attend = parseQueryString('a=2&a=3&a=2&b=3&1=4&d=5&e')
attend.
type C =ParseQueryString<'a=2&a=3&a=2&b=3&true=4&d=5&e'>
比如:模块 类 装饰器 命名空间 declare 模版字符串 等等
阮一峰作 https://wangdoc.com/typescript/comment
多看多学多写
https://juejin.cn/post/7236670795061297209?from=search-suggest https://juejin.cn/post/7235966403064725564?searchId=202309040030580DD385449710E2A6EFE0#
function comp<Type>(a:Type, b:Type) {
if (a.length >= b.length) {
return a;
}
return b;
}
function comp1<Type extends {length:number}>(a:Type, b:Type) {
if (a.length >= b.length) {
return a;
}
return b;
}
comp1([1, 2], [1, 2, 3]) // 正确
comp1('ab', 'abc') // 正确
comp1(1, 2) // 报错
function deb<T extends any[],R>(fn:(...args:T)=>R,time:number):(...args:T)=>void {
return ()=>{}
}
function handle(a:number,b:string){ return [a,b] }
const f = deb(handle,200)
f(1,'1')
type MyObj = { foo: number, bar: string, age:{
a:string
}};
type Keys = keyof MyObj; // 'foo'|'bar'
let case1:Keys='foo';
type Case2 = {
[P in keyof MyObj]:string
}
type MyReadonly1<T> ={
+readonly[P in keyof T]:T[P]
}
type MyRequired<T> ={
[P in keyof T]-?:T[P]
}
type MyPartial<T> ={
[P in keyof T]+?:T[P]
}
type MydeepPartial<T> ={
[P in keyof T]+?:T[P] extends Object?MydeepPartial<T[P]>:T[P]
}
type case4 = MydeepPartial<MyObj>
let case5:case4={
age:{
}
}
type LiteralTypeName<T> =
T extends undefined ? "undefined" :
T extends null ? "null" :
T extends boolean ? "boolean" :
T extends number ? "number" :
T extends bigint ? "bigint" :
T extends string ? "string" :
never;
type Result2 = LiteralTypeName<true | 1 | 'a'>;
// "boolean"|'number'|'string'
type myexclude<T,U>=T extends U?never:T
type myextract<T,U>=U extends T?T:never
type Mypick<T,U extends keyof T>={
[P in U]:T[P]
}
type MyOmit<T,U>={
[P in myexclude<keyof T,U>]:T[P]
}
type MyOmit1<T,U>=Mypick<T,myexclude<keyof T,U>>
type MyType<T,> =
T extends {
a: U,
b:K
} ? [M, N] : never;
type T = MyType<{ a: string; b: number }>;
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
const picked: Partial<T> = {};
keys.forEach((key) => {
picked[key] = obj[key];
});
return picked as Pick<T, K>;
}
const user = {
id: 1,
name: "Alice",
age: 25,
email: "[email protected]",
};
const pickedUser = pick(user, ["name", "email"]);
pickedUser.name
type case6 = Record<keyof any,any>
type StartsWith<T extends string,U extends string>=T extends `${U}${infer P}`?true:false;
type a = StartsWith<'abc', 'ab'>
type a2 = StartsWith<'abc', 'ac'>
type endsWith<T extends string,U extends string>=T extends `${infer P}${U}`?true:false;
type IndexOf<T extends any[], U, R extends any[] = []> =
T extends [infer K, ...infer Rest]
? K extends U
? R['length']
: IndexOf<Rest, U, [...R, 1,1,1,1,1,]>
: -1
type MergeValues<One, Other> =
One extends Other
? One
: Other extends unknown[]?
IndexOf<Other,One> extends -1?
[One, ...Other]
: Other
:[One, Other]
type MergeParams<
OneParam extends Record<string, any>,
OtherParam extends Record<string, any>
> = {
[Key in keyof OneParam | keyof OtherParam]:
Key extends keyof OneParam
? Key extends keyof OtherParam
? MergeValues<OneParam[Key], OtherParam[Key]>
: OneParam[Key]
: Key extends keyof OtherParam
? OtherParam[Key]
: never
}
type ParseParam<Param extends string> =
Param extends `${infer Key}=${infer Value}` //a 1=>{a:1}
? {
[p in Key]: Value
} : Param extends '' ? {} : { [K in Param]: true }
// Record<string, any>;
type ParseQueryString<Str extends string>
= Str extends `${infer Param}&${infer Rest}`
? MergeParams<ParseParam<Param>, ParseQueryString<Rest>>
: ParseParam<Str>; //'a=1' {a:1}
function parseQueryString<T extends string>(queryStr:T): ParseQueryString<T> {
if (!queryStr || !queryStr.length) {
return {} as ParseQueryString<T>;
}
const queryObj = {};
const items = queryStr.split('&');
items.forEach((item) => {
const [key, value] = item.split('=');
if (queryObj[key]) {
if(Array.isArray(queryObj[key])) {
queryObj[key].push(value);
} else {
queryObj[key] = [queryObj[key], value]
}
} else {
queryObj[key] = value;
}
});
return queryObj as any;
}
const attend = parseQueryString('a=2&a=3&a=2&b=3&1=4&d=5&e')
// attend.
type C =ParseQueryString<'a=2&a=3&a=2&b=3&true=4&d=5&e'>