繁星永存 记忆亘古不变

认识泛型

软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能

在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件

需要方法使返回值的类型与传入参数的类型是相同,使用 类型变量,特殊的变量,只表示类型而不是值

function foo<Type>(arg: <Type>): Type{
return arg
}
// 参数类型为 number
const num = foo<number>(123)
// string
const str = foo<string>('hello')
// 类型推论,根据传入的参数自动确定Type的类型
const hello = foo('hello world') // type of output will be 'string'

foo 添加类型变量Type ,帮助我们捕获用户传入的类型(比如:number),我们再次使用 Tyep 当做返回值类型,知道参数类型与返回值类型是相同的

平时开发时没必要手动使用<类型>传,因为类型推论可以自动完成
如果编译器不能够自动地推断出类型的,才手动传入

默认值,像函数参数一样也可以赋上默认值

function foo<Type = string>(arg: Type): Type{
return arg
}

平常使用类型变量名的缩写:

  • T : Type的缩写
  • K : key的缩写
  • V : value的缩写
  • E : Element的缩写
  • O : Object的缩写

泛型接口

在定义接口的时候我们也可以使用泛型

interface IFoo<T = number> {
id: T,
valueList: T[],
handleValue: (value: T) => void
}

const foo: IFoo<number> = {
id: 101,
valueList: [1,2,3]
handleValue: function(value: number){
console.log(value)
}
}

泛型类

泛型类与泛型接口差不多。 泛型类使用<>括起泛型类型,跟在类名后面

class Point<T> {
x: T
y: T

constructor(x: T, y: T){
this.x = x
this.y = y
}

}
// 类型推论
const p1 = new Point(10,20)
// 明确
const p2 = new Point<number>(10,20)
const p3: Point<number> = new Point(10,20)

泛型约束(Generic Constraints)

希望传入的类型有某些共性,但是这些共性可能不是在同一种类型中

  • 比如string和array都是有length的,或者某些对象也是会有length属性的
  • 那么拥有length的属性都可以作为参数类型
interface ILength {
length: number
}
function foo<T extends ILength>(arg: T): T{
console.log(arg.length);
}

//泛型函数被定义了约束,不再是适用于任意类型
foo(123) // error
// 符合约束类型的值,必须包含必须的属性
foo({length: 10, value: 20})

在泛型约束中使用类型参数
举个栗子:我们希望获取一个对象给定属性名的值

  • 我们需要确保我们不会获取 obj 上不存在的属性
  • 所以我们在两个类型之间建立一个约束
// 声明Type类型参数,Key 类型参数继承 Type里的key
function foo<Type, Key extends Type keyof Type>(obj: Type, key: Key){
console.log(obj[key])
}

const info = {
name: 'kun'
age: 12
}

foo(info,"name")
foo(info,"age")

映射类型(Mapped Types)

一个类型需要基于另外一个类型,但你又不想拷贝一份,这个时候可以考虑使用映射类型。

  • 大部分内置的工具都是通过映射类型来实现
  • 大多数类型体操的题目也是通过映射类型完成

映射类型建立在索引签名上

  • 映射类型,就是使用了 PropertyKeys 联合类型的泛型
  • PropertyKeys 多是通过 keyof 创建,然后循环遍历
interface IPerson {
name: string
age: number
}

type MapType<Type> = {
[property in keyof Type]: any
}
type NewPerson = MapType<IPerson>

映射修饰符(Mapping Modifiers)

在使用映射类型时,有两个额外的修饰符可能会用到:

  • readonly: 用于设置属性只读;
  • ? : 用于设置属性可选;

修饰符可添加前缀 - 或者 + 删除或者添加这些修饰符
如果没有写前缀,相当于使用了 + 前缀

类型体操练习网站

网站1
网站2

条件类型(Conditional Types)

条件类型(Conditional types)就是用来帮助我们描述输入类型和输出类型之间的关系
写法类似 三目运算符
SomeType extends OtherType ? TrueType : FalseType

function foo<T extends number | string>(arg1: T, arg2: T):T extends string?string:number{
console.log(arg+arg2)
}

评论



本站使用 Volantis 主题设计

:doodle { @grid: 1x5 / 100vmin; } @place-cell: center; width: @rand(45vmin, 75vmin); height: @rand(45vmin, 75vmin); transform: translate(@rand(-120%, 120%), @rand(-80%, 80%)) scale(@rand(.8, 2.8)) skew(@rand(45deg)); clip-path: polygon( @r(0, 30%) @r(0, 50%), @r(30%, 60%) @r(0%, 30%), @r(60%, 100%) @r(0%, 50%), @r(60%, 100%) @r(50%, 100%), @r(30%, 60%) @r(60%, 100%), @r(0, 30%) @r(60%, 100%) ); background: @pick(#f44336, #9c27b0, #673ab7, #3f51b5, #60569e, #e6437d, #ebbf4d, #00bcd4, #03a9f4, #2196f3, #009688, #5ee463, #f8e645, #ffc107, #ff5722, #43f8bf, #e136eb, #32ed39); opacity: @rand(.5, .9); position: relative; top: @rand(-80%, 80%); left: @rand(0%, 80%); animation: colorChange @rand(6.1s, 26.1s) infinite @rand(-.5s, -2.5s) linear alternate; @keyframes colorChange { 100% { left: 0; top: 0; filter: hue-rotate(360deg); } }