ts简单学习

type 与 interface 异同

  • type 能表示的任何类型组合。
    • 定义基本类型的别名
    • 通过 typeof 操作符来定义
    • 可以申明联合类型 |
    • 可以申明元祖类型
  • interface 只能表示对象结构的类型。
    • interface 可以描述函数签名
  • interface 可以继承(extends)另一个 interface。
    • extends 可以将属性的类型进行收窄,比如从 string | number 变成 string。但声明合并不行,类型必须完全一致。
  • interface 也可以继承自 type,但只能是对象结构,或多个对象组成交叉类型(&)的 type。
  • type 可以通过 & 的写法来继承 type 或 interface,得到一个交叉类型
  • interface 支持声明合并,文件下多个同名的 interface,它们的属性会进行合并。type类型不支持
    • 同名属性的不能进行类型覆盖修改
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 会把每一个元素单独传入来做类型运算,最后再合并成联合类型,这种语法叫做分布式条件类型

触发分布式条件类型三条件

  1. 得是个联合类型
  2. 联合类型通过泛型参数的形式传入
  3. 泛型参数在条件类型语句中需要是裸露类型(没有被[]包裹)
//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% 的人说不出原因 - 掘金

Utility Types 内置类型工具

Record

TypeScript 中的AnyRecord采用 形式Record,其中K是键的类型,T是值的类型。上面,我们为我们的值定义了一个新类型User,并将我们的键设置为类型string。

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},
}

Partial

构造一个所有属性都设置为可选的类型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 }

Required

我们有时需要强制对象具有必需的属性,即使原始类型将其中一些属性定义为可选的。为此,TypeScript 有一个名为Required

type User = {
  name?: string,
  age?: number
}
//类型 "{ name: string; }" 中缺少属性 "age",但类型 "Required" 中需要该属性
// const a: Required = {
//    name:"技术"
// }
const a: Required = {
  name: "技术",
  age:12
}

Readonly

构造一个所有属性都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 = '小小'

Pick

通过从 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:''
}

Omit

通过从 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
}

Exclude

通过从 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;

Extract

通过从 Type 中提取可分配给 Union 的所有联合成员来构造一个类型。

type User = string | number | null
type User1 = Extract
//number
const a: User1 = 1;

NonNullable

通过排除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

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";}

你可能感兴趣的:(typeScript,JS,前端,学习,javascript,前端,typescript)