Skip to content

Latest commit

 

History

History
514 lines (427 loc) · 12.2 KB

分享格式化.md

File metadata and controls

514 lines (427 loc) · 12.2 KB

Ts高级技巧分享大纲

Ts 编译时态 的类型检查工具 基础 pass 略过,直接从泛型讲

泛型基础

泛型可以理解成一段类型逻辑,需要类型参数来表达。有了类型参数以后,可以在输入类型与输出类型之间,建立一一对应关系

  • 函数
  • 接口
  • 类型别名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

类型参数的约束条件 extends

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

精确表达对象的属性类型 属性映射,即将一个类型的所有属性逐一映射成其他值 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" | ···

in取出(遍历)联合类型的每一个成员类型

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'];

extends ..? : 分发

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 代表待推断类型

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

Ts学习技巧

多看多学多写

参考文献

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'>