TS 基础学习(四)

交叉类型
  交叉类型(&): 功能类似于接口继承(extends),用于组合多个类型为一个类型(常用与对象类型)
交叉类型(&)和接口继承(extends)的对比:
  1、相同点: 都可以实现对象类型的组合
  2、不同点: 两种方式实现类型组合时, 对于同名属性之间,处理类型冲突的方式不同
泛型和keyof
  泛型是可以在保证类型安全前提下,让函数等与多种类型一起工作,从而实现复用,常用于:函数、接口、class中。
  泛型函数
  // 定义一个泛型函数
  function id<Type>(value: Type): Type { return value}
  1、语法: 在函数名称后面添加<>,尖括号中添加类型变量,比如此处的Type
  2、类型变量Type, 是一种特殊类型的变量,它处理类型而不是值
  3、该类型变量相当于一个类型容易,能够捕获用户提供的类型(具体是什么类型由用户调用该函数时指定)
  4、因为Type是类型,因此可以将其作为函数参数和返回值类型,表示参数和返回值具有相同的类型
  5、类型变量Type,可以是任意合法的变量名称
  // 调用泛型函数
  const num = id<number>(10)
  const str = id<string>('str')
  6、语法: 在函数名称后面添加<>,尖括号中指定具体的类型,比如此处的number
  7、当传入类型number后,这个类型就会被函数声明时指定的类型变量Type捕获到。
  8、此时,Type的类型就是number,所以,函数id参数和返回值类型也是number。
  9、同样,如果传入类型string, 函数id参数和返回值类型都是string。
  这样通过 泛型就做到了让id函数与多种不同的类型一起工作了,实现了复用的同时保证了类型安全。
  10、在调用泛型函数时,可以省略<类型>来监护泛型函数的调用
  11、此时,TS内部会采用一种叫做类型参数推断的机制,来根据传入的实参自动判断出类型变量Type的类型。
  12、推荐: 使用这种简化的方式调用泛型函数,使代码更短、更已于阅读。
  13、说明: 当编译器无法推断类型或者推断的类型不准确时,就需要显示传入类型参数。
泛型约束
  泛型约束:默认情况下,泛型函数的类型变量Type可以代表多个类型,这导致无法访问任何属性。
  Type可以代表任意类型, 无法保证一定存在length属性,比如number类型就没有length。此时就需要为泛型添加约束来收缩类型(缩窄类型取值范围)。
  添加泛型约束收缩类型,主要有以下两种方式: 1.指定更加具体的类型; 2. 添加约束
  添加约束:
  // 定义一个接口 接口中有一个属性length
  interface ILength { length: number}

  function id<Type extends ILength>(value: Type): Type {
       console.log(value.length);
       return value;
  }
  1、创建描述约束的接口ILength,该接口要求提供length属性
  2、通过extends关键字使用该接口,为泛型(类型变量)添加约束
  3、该约束表示:传入的类型必须具有length属性
  注意: 传入的实参(比如,数组)只要有length属性即可,这也符合前面讲到的接口的兼容性
  注意:此处的extends不是继承的意思,是Type要满足ILength的接口约束(这里传入的参数必须满足具有一个叫length属性的约束)
  泛型的类型变量可以有多个,并且类型变量之间还可以约束(比如,第二个类型变量受第一个类型变量约束)。比如,创建一个函数来回去对象属性的值:
  1、添加了第二个类型变量Key,两个类型变量之间使用(,)逗号分隔
  2、keyof关键字接受一个对象类型,生成其名称(可能是字符串或数字)的联合类型
  3、本示例中keyof Type实际上获取的是person对象所有的联合类型,也就是name | age | gender
  4、类型变量Key受Type约束,可以理解为: Key只能是Type所有键中的任意一个,或者说只能访问对象中存在的属性
泛型接口
  泛型接口: 接口也可以配合泛型来使用, 以增加其灵活性,增强其复用性。
  // 定义一个泛型接口
  interface IdFunc<Type> {
     id: (value: Type) => Type
     ids: () => Type[]
   }

   let obj: IdFunc<number> = {
       id(value) {
          return value
    },

    ids() {
       return [1, 3, 5]
    },
   }
  1、在接口名称的后面添加<类型变量>,那么,这个接口就变成了泛型接口
  2、接口的类型变量,对接口中所有其他成员可见, 也就是接口中所有成员都可以使用类型变量。
  3、使用泛型接口,需要显示指定具体的类型(比如,此处的idFunc)
  4、此时,id方法的参数和返回值类型都是number;ids方法的返回值类型是number[].
  当我们在使用数组时,TS会根据数组的不同类型,来自动将类型变量设置为相应的类型
泛型类
  泛型类:class也可以配合泛型来使用。比如React的class组件的基类Component就是泛型类型
  React.Component泛型类两个类型变量,分别指定props和state类型
  class GenericNumber<NumType> {
    defaultValue: NumType
    add: (x: NumType, y: NumType) => NumType
  }
  1、类似于泛型接口,在class名称后面添加<类型变量>这个类就变成了反省类。
  2、此处的add方法,采用的是箭头函数形式的类型书写方式。
  const myNum = new GenericNumber<number>()
  myNum.defaultValue = 0
泛型工具类
  泛型工具类:TS内置了一些常用的工具类型,来简化TS中的一些常规操作
1、Partial<Type>
  作用:Partial接收一个泛型类型Type,并将Type所有属性都设置为可选的,返回构造的新类型。
  ts复制代码interface User {
      name: string
      age: number
      address: string
  }
  function updateUser(user: User, fieldsToUpdate: Partial<User>) {
     return { ...user, ...fieldsToUpdate }
  }
  const user: User = { name: 'xiaoming', age: 30, address: '上海' }
  const newUser = updateUser(user, { address: '北京' })
2、Required<Type>
  作用:Required接收一个泛型类型Type,并将Type所有属性都设置为必选的,返回构造的新类型(Required的作用与Partial相反)。
  ts复制代码interface Props {
     name?: string
     age?: number
   }
  function printProps(props: Required<Props>) {
      console.log(`${props.name} is ${props.age} years old.`)
  }
  printProps({ name: 'xiaoming'}) // error ❗️
3、Readonly<Type>
  作用:Readonly接收一个泛型类型Type,并将Type所有属性都设置为只读的,返回构造的新类型, 新类型的属性不可再进行分配。
  ts复制代码interface User {
    name: string
    age: number
   }
  const user: Readonly<User> = { name: 'xiaoming', age: 30 }
  user.name = 'zhangsan' // error ❗️
4、Record<Keys, Type>
  作用:构造一个对象类型,其属性键为Keys,属性值为Type。
  ts复制代码interface User {
     name: string
    age: number
   }
  type UserName = 'xiaoming' | 'xiaohong' | 'xiaohuang'
  const users: Record<UserName, User> = {
  xiaoming: { name: 'ming', age: 23 },
  xiaohong: { name: 'hong', age: 24 },
  xiaohuang: { name: 'huang', age: 25 }
  }
5、Pick<Type, Keys>
  作用: 从类型Type中选择一组属性Keys来创建类型。
  复制代码interface User {
    name: string;
    age: number;
    address: string
  }

  type NameAndAgeOnly = Pick<User, 'name' | 'age'>;
  const nameAndAgeOnly: NameAndAgeOnly = { name: 'xiaoming', age: 26 };
  扩展:
  结合pick函数使用
  ts复制代码const user = {name: 'xiaoming', age: 26, address: 'shanghai'}
  declare function pick<T, K extends keyof T>(obj: T, ...keys: K[]): Pick<T, K>;
  const nameAndAgeOnly = pick(user, "name", "age"); // { name: string, age: number }

6、xclude<UnionType, ExcludedMembers>
  作用: 从联合类型UnionType中排除ExcludedMembers类型然后返回一个新类型。
  ts复制代码interface User {
     name: string;
     age: number;
     address: string
  }

  type UserExcludeAddress = Exclude<keyof User, 'address'> // "name" | "age"
  扩展:
  ts复制代码const user1 = { name: 'ming', age: 23, address: 'shanghai' } as const
  const user2 = { name: 'hong', age: 24, address: 'beijing' } as const
  const user3 = { name: 'huang', age: 25, address: 'shenzhen' } as const

  type Users = typeof user1 | typeof user2 | typeof user3

  type UsersExcludeMing = Exclude<Users, { name: 'ming' }>
7、Extract<Type, Union>
  作用: 从联合类型Type中提取Union类型然后返回一个新类型。
  ts复制代码interface User {
      name: string;
      age: number;
      address: string
   }
  type UserAddress = Extract<keyof User, 'address'> // address

  type Person = {
       name: string;
      age: string;
  }
  const user: Extract<keyof User, keyof Person> = 'name' || 'age';

  type SuccessCode = Extract<200 | 404, 200>; // 200
  扩展:
  如果泛型Type中没有Union, 返回never。
  ts复制代码type SuccessCode = Extract<200 | 404, 204>; // never
8、Omit<Type, Keys>
  作用: 与Pick相反,Omit是从Type中选取所有Keys属性然后删除构造一个新类型。
  ts复制代码interface User {
      name: string;
     age: number;
     address: string
  }

  type UserOmitAge = Omit<User, 'address'>;

  const userOmitAge: UserOmitAge = { name: 'xiaoming', age: 30 };
  扩展:
  从源码可以看出来Omit的实现其实就是Pick和Exclude的组合。因为Omit的使用场景比较多,所以Typescript应使用者要求新增了Omit工具类型。
9、NonNullable<Type>
  作用: 通过从Type中排除null和undefined来构造一个类型。
  ts复制代码type PortNumber = string | number | null;
  type ServerPortNum = NonNullable<PortNumber>
10、Parameters<Type>
  作用: 接受一个函数类型, 将函数的参数处理成一个元组类型
  ts复制代码function createStudent(sno: string, name: string, age: number) {
     return { sno, name, age }
  }

  type CreateStudentParams = Parameters<typeof createStudent>

  const createStuParams: CreateStudentParams = ['112899022', 'ming', 30]
  const stu1 = createStudent(...createStuParams)
  扩展:
  Parameters接收的泛型必须是一个函数类型,但是作为顶级类型any和底层类型never, Typescript对其有特殊的处理。
  ts复制代码type T1 = Parameters<any>; // unknown[]
  type T2 = Parameters<never>; // never
  Typescript中定义的Function也不能被parameters接收,看Function 的实现就知道了。
  复制代码type T7 = Parameters<Function>; // error
  Function的实现:
  ts复制代码interface Function {
      readonly name: string;
   }
  如果函数是重载函数,那么返回的是最后一个函数的参数组成的元组类型
  ts复制代码declare function stringOrNum(x: string): number;
  declare function stringOrNum(x: number): string;
  declare function stringOrNum(x: string | number): string | number;

   type T1 = Parameters<typeof stringOrNum>; // [x: string | number]
11、ConstructorParameters<Type>
  作用: 接受一个具有构造函数的类型, 将构造函数的参数处理成一个元组类型。
  ts复制代码class Test { constructor(a: number, b: string) {}}
  type T1 = ConstructorParameters<typeof Test>; // [a: number, b: string]

  type T2 = ConstructorParameters<new (x: string, y: number) => any> // [x: string, y: number]
12、ReturnType<Type>
  作用: 获取函数类型的返回值类型。
  复制代码function getUser() {
     return { name: 'ming', age: 30 }
   }

   type User = ReturnType<typeof getUser>

   const user: User = { name: 'hong', age: 26 }
  扩展:

  同Parameters类型一样,接收的泛型必须是一个函数类型
  泛型是any和never的情况
  ts复制代码type T1 = ReturnType<any>; // any
  type T2 = ReturnType<never>; // never
  Function不能被ReturnType接收
  如果函数是重载函数,那么返回的是最后一个函数的返回类型。
13、InstanceType<Type>
  作用: 获取构造函数类型的返回类型(构造函数返回什么什么类型,InstanceType获取的就是什么类型)。
  ts复制代码class Person {
      constructor(public name: string) {}
   }
   type PersonInstance = InstanceType<typeof Person>
   const person: PersonInstance = new Person('Alice')

   interface User {
       new (name: string): Object
    }
  type UserInstance = InstanceType<User> // Object
  扩展:

  InstanceType也常用于获取内置构造方法或第三方库的参数类型
  ts复制代码type T3 = InstanceType<ErrorConstructor> // Error
  type T4 = InstanceType<FunctionConstructor> // Function
  type T5 = InstanceType<RegExpConstructor> // RegExp
  对any和never的特殊处理
  ts复制代码type T7 = InstanceType<any>;
  type T8 = InstanceType<never>;
  一个vue的例子
  ts复制代码<!-- MyModal.vue -->
  <script setup lang="ts">
  import { ref } from 'vue'

  const isContentShown = ref(false)
  const open = () => (isContentShown.value = true)

 defineExpose({
  open
  })
  </script>
  // 为了获得MyModal的实例类型,使用InstanceType实用程序提取其实例类型
  <!-- Other.vue -->
  <script setup lang="ts">
  import MyModal from './MyModal.vue'
  const modal = ref<InstanceType<typeof MyModal> | null>(null)
  const openModal = () => {
    modal.value?.open()
  }
  </script>
14、Awaited<Type>
  作用: 获取Promise中的类型(如await、then方法返回的被Promise包裹的数据的类型)。适合处理异步操作并确保解析值的类型安全。
  ts复制代码type A = Awaited<Promise<string>>; // string
  type B = Awaited<Promise<Promise<number>>>; // number

  // 假如这是一个第三方库,User没有导出,fetchUser函数导出了
  interface User {
    name: string
    age: number
  }
  export async function fetchUser(): Promise<User> {
  const data = await fetch('https://www.example.com/user').then(res => {
        return res.json()
  })
  return data
 }

  // 我们开发中在获取到了User类型
  type UserFetch = Awaited<ReturnType<typeof fetchUser>>

  async function getUserInfo() {
       let user: UserFetch = { name: 'ming', age: 30 }
       return user
  }
 扩展:

  Awaited是可以递归解Promise
  type T1 = Awaited<Promise<Promise<Promise<Promise<Promise<number>>>>>> // number
  如果传入Awaited的泛型不是Promise,那么会原样返回
  ts
  复制代码type T2 = Awaited<string> // string
15、ThisParameterType<Type>
  作用: 提取函数类型的this参数的类型, 如果函数类型没有this参数, 返回unknown。
  ts复制代码function toHex(this: Number) {
      return this.toString(16);
   }

  function numberToString(n: ThisParameterType<typeof toHex>) {
     return toHex.apply(n);
  }
16、OmitThisParameter<Type>
  作用: 与ThisParameterType相反, 排除函数类型的this参数
  ts复制代码function toHex(this: Number) {
  return this.toString(16);
  }
  const fiveToHex: OmitThisParameter<typeof toHex> = toHex.bind(5);
  console.log(fiveToHex());
17、ThisType<Type>
  作用: 控制字面量对象中this所表示的类型。 只在--noImplicitThis下有用
  ts复制代码// 正常情况推导出来的this
  type Point = {
      x: number;
      y: number;
     moveBy(dx: number, dy: number): void;
  }

   let p: Point = {
    x: 10,
    y: 20,
   moveBy(dx, dy) {
   this.x += dx; // this has type Point
   this.y += dy; // this has type Point
  }
  }
18、 ReadonlyArray<Type>
  作用: 描述只能读的数组, 不可进行添加、删除、替换操作。
  ts复制代码function foo(arr: ReadonlyArray<string>) {
  arr.slice(); // okay
  arr.push("hello!"); // Type error
  arr.pop(); // Type error
  arr.splice(1, 1); // Type error
  arr[0] = 'aa' // Type error
  }
  foo(['a','b','c'])
  ReadonlyArray<string>`的简写:`readonly string[]
19、Uppercase<StringType>
  作用: 将字符串中的每个字符转换为对应的大写。
  ts复制代码type Greeting = 'Hello, world'
  type ShoutyGreeting = Uppercase<Greeting> // "HELLO, WORLD"

  type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`
  type MainID = ASCIICacheKey<'my_app'> // "ID-MY_APP"
20、Lowercase<StringType>
  作用: 将字符串中的每个字符转换为对应的小写。
  ts复制代码type Greeting = 'Hello, world'
  type QuietGreeting = Lowercase<Greeting> // "hello, world"

  type ASCIICacheKey<Str extends string> = `id-${Lowercase<Str>}`
  type MainID = ASCIICacheKey<'MY_APP'> // "id-my_app"
21、Capitalize<StringType>
  作用: 将字符串中的第一个字符转换为大写字母
  ts复制代码/**
  * Convert first character of string literal type to lowercase
  */
  type Uncapitalize<S extends string> = intrinsic;
22、Uncapitalize<StringType>
作用: 将字符串中的第一个字符转换为小写字母。
ts复制代码type UppercaseGreeting = "HELLO WORLD";
type UncomfortableGreeting = Uncapitalize<UppercaseGreeting>;
优秀的类型库
  utility-types
  type-festts-toolbelt
  ts-toolbelt

posted @ 2024-06-28 11:38  等风来灬  阅读(7)  评论(0编辑  收藏  举报