【查漏补缺系列-ts篇】泛型、ts中的各种断言

207 阅读4分钟

前言: 本类文章初衷只用作记录个人的学习博客,哪里有漏补哪里,不做任何其他商业用途。欢迎讨论,不喜勿喷。后面要是有遗漏的相关知识点,也会相应的补上。如果本篇文章能帮到你,我也会很愉快,共勉😁

目录

  1. 编译环境
  2. 泛型
  3. ts中的各种断言

泛型

设计泛型的关键目的是在成员之间提供有意义的约束,这些成员可以是:

  • 类的实例成员
  • 类的方法
  • 函数参数
  • 函数返回值

泛型用来创建可重用的组件、函数、类,一个组件可以支持多种类型的数据。这样用户就可以根据自己的数据类型来使用组件。

基础用法

function identity<T>(arg: T): T {
  return arg
}

// 将函数参数和泛型的类型参数一并传入
const ida = identity<string>('Hello') // Hello

// 只传入参数(推荐)
const idb = identity('Hello') // Hello

第一种方式是直接将函数参数与类型参数一并传入。第二种只传入函数参数,通过ts的类型推论来自动确定T的类型。

误用的泛型

当你使用泛型时,应该先确定它能提供什么样的约束。如:

function foo<T>(arg: T): void;

在这种情况下,泛型完全没有必要使用,因为它仅用于单个参数的位置,使用如下方式可能更好:

function foo(arg: any): void;

泛型接口

可以将泛型写在函数类型的接口中。

interface PropsTarget {
  <T>(arg: T): T
}

const identity = <T>(args: T): T => {
  return args
}

let rewriteIdentity: PropsTarget = identity;

也可以将泛型参数当做整个接口的一个参数。


interface PropsTarget<T> {
  (arg: T): T
}

const identity = <T>(args: T): T => {
  return args
}

let rewriteIdentity: PropsTarget<string>= identity;

泛型类

class Person<T> {
  name: T;
  constructor(name: T) {
    this.name = name
  }
  run(value: T): T {
    return value
  }


}

const p1 = new Person<string>('Jerry')

console.log(p1.run('Hello')) // Hello

泛型约束

泛型可以传入any任意类型,我们可以给某个泛型来创造约束条件,如下:

interface PropsTarget {
  value: string
} 

const logging = <T extends PropsTarget>(arg: T): T => arg

logging({ value: 'Hello' }) // { value: 'Hello' }

我们希望传入的类型必须包含一个string类型的value属性,可以使用interfaceextends来实现。

泛型约束中使用类型参数

你可以声明一个类型参数,且它被另一个类型参数所约束。比如,现在我们想要用属性名从对象里获取这个属性。 并且我们想要确保这个属性存在于对象 obj 上,因此我们需要在这两个类型之间使用约束。

const getProperty = <T, U extends keyof T>(obj: T, key: U) => {
  return obj[key]
}

const target = { a: 1, b: 2, c: 3, d: 4 }

getProperty(target, 'a') // 1
getProperty(target, 'm') // error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'.

ts中的各种断言

类型断言

有时候你会遇到这样的情况,你会比 TypeScript 更了解某个值的详细信息。 通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。 通过类型断言可以告诉编译器, "我知道自己在干什么"。它没有运行时的影响,只是在编译阶段起作用。ts会假设你已经进行了必须的检查。 类型断言有两种形式,它们都是等价的:

  • <>尖括号写法
  • as 语法
let someValue: any = 'this is a string'

let strLengthA: number = (<string>someValue).length

let strLengthB: number = (someValue as string).length

非空断言

由于可以为null的类型是通过联合类型实现,那么你需要使用类型守卫来去除null

const run = (msg: string | null): string => {
  return msg // Error 这里不能将string | null分配给类型string
}

通过短路运算符去除null

const run = (msg: string | null): string => {
  return msg || 'default' 
}

通过非空断言手段去除,语法是添加!后缀。可以用于断言操作对象是非null和非undefined类型。

const run = (msg: string | null): string => {
  return msg!
}

const 断言

TypeScript 3.4 引入了一个叫 const 断言的字面量值的新构造。 它的语法是用 const 代替类型名称的类型断言(例如 123 as const )。'const' 断言只能用在 string, number, boolean, array, object literal。当我们用 const 断言构造新的字面量表达式时,我们可以用来表示:

  • 该表达式中的字面量类型不应粗化(例如,不要从 'hello' 到 string )
  • 对象字面量获得 readonly 属性
  • 数组字面量成为 readonly 元组

字符串、数值、布尔值字面量

//  Type: 1 字面量类型除了全等的赋值无法被赋予任何其他值
let a = 1 as const

a = 2 // Error 不能将类型2分配给类型1

let b = 'Hello' as const
let c = true as const

对象字面量

let d = { text: 'hello' } as const // 会被转换为 { readonly text: 'hello'}

d.text = 1 // 无法为text赋值,它只是只读属性
d.msg = '' // 类型{ readonly text: 'hello'}上不存在属性msg

数组字面量

let e = [10, 20] as const

e[3] // Error:长度为2的元组类型 readonly [10, 20]在索引3处没有元素

也可以使用尖括号断言语法,除了.tsx文件之外。

let e = <const>[10, 20]