暮年|细数这些年被困扰过的 TS 问题( 二 )


interface LooseObject {[key: string]: any}该类型使用 索引签名 的形式描述 LooseObject 类型可以接受 key 类型是字符串 , 值的类型是 any 类型的字段 。 有了 LooseObject 类型之后 , 我们就可以通过以下方式来解决上述问题:
interface LooseObject {[key: string]: any}let developer: LooseObject = {};developer.name = "semlinker";对于 LooseObject 类型来说 , 它的约束是很宽松的 。 在一些应用场景中 , 我们除了希望能支持动态的属性之外 , 也希望能够声明一些必选和可选的属性 。
比如对于一个表示开发者的 Developer 接口来说 , 我们希望它的 name 属性是必填 , 而 age 属性是可选的 , 此外还支持动态地设置字符串类型的属性 。 针对这个需求我们可以这样做:
interface Developer {name: string;age?: number;[key: string]: any}let developer: Developer = { name: "semlinker" };developer.age = 30;developer.city = "XiaMen";其实除了使用 索引签名 之外 , 我们也可以使用 TypeScript 内置的工具类型 Record 来定义 Developer 接口:
// type Record = { [P in K]: T; }interface Developer extends Record {name: string;age?: number;}let developer: Developer = { name: "semlinker" };developer.age = 30;developer.city = "XiaMen";三、如何理解泛型中的对于刚接触 TypeScript 泛型的读者来说 , 首次看到语法会感到陌生 。 其实它没有什么特别 , 就像传递参数一样 , 我们传递了我们想要用于特定函数调用的类型 。
暮年|细数这些年被困扰过的 TS 问题参考上面的图片 , 当我们调用 identity(1), Number 类型就像参数 1 一样 , 它将在出现 T 的任何位置填充该类型 。 图中内部的 T 被称为类型变量 , 它是我们希望传递给 identity 函数的类型占位符 , 同时它被分配给 value 参数用来代替它的类型:此时 T 充当的是类型 , 而不是特定的 Number 类型 。
其中 T 代表 Type , 在定义泛型时通常用作第一个类型变量名称 。 但实际上T 可以用任何有效名称代替 。 除了 T 之外 , 以下是常见泛型变量代表的意思:

  • K(Key):表示对象中的键类型;
  • V(Value):表示对象中的值类型;
  • E(Element):表示元素类型 。
其实并不是只能定义一个类型变量 , 我们可以引入希望定义的任何数量的类型变量 。 比如我们引入一个新的类型变量 U , 用于扩展我们定义的 identity函数:
function identity (value: T, message: U) : T {console.log(message);return value;}console.log(identity(68, "Semlinker"));
暮年|细数这些年被困扰过的 TS 问题除了为类型变量显式设定值之外 , 一种更常见的做法是使编译器自动选择这些类型 , 从而使代码更简洁 。 我们可以完全省略尖括号 , 比如:
function identity (value: T, message: U) : T {console.log(message);return value;}console.log(identity(68, "Semlinker"));对于上述代码 , 编译器足够聪明 , 能够知道我们的参数类型 , 并将它们赋值给 T 和 U , 而不需要开发人员显式指定它们 。
继续阅读:一文读懂 TypeScript 泛型及应用
四、如何理解装饰器的作用在 TypeScript 中装饰器分为类装饰器、属性装饰器、方法装饰器和参数装饰器四大类 。 装饰器的本质是一个函数 , 通过装饰器我们可以方便地定义与对象相关的元数据 。