· 1062 words · 6 min
字面量(literal)用于在代码中表示一个固定值(区别于变量,变量是可变化的),TypeScript 中也有字面量类型。分为 string 字面量类型、number 字面量类型、boolean 字面量类型等。 一般情况下,字面量类型可以看做是对应的类型的子类型。比如 string 字面量类型可以认为是 string 类型的子类型。在这种前提下我们可以得知,字面量类型是可以赋值给对应父类型的,反之则不行(除非用 as)。
let hello: string = ''; // string type
let ts: 'TypeScript' = 'TypeScript'; // string literal type
hello = ts; // OK
ts = hello; // Error: Type '"TypeScript"' is not assignable to type 'string'
TypeScript 中,如果一个字面量是以 const 关键字声明的,那么在使用 typeof 获取类型的时候,返回的是字面量类型。而以 let、var 等关键字声明的字面量,typeof 返回的类型是字面量的父类型。这和 const 的本质禁止再次修改赋值是一一对应的,因此 TypeScript 能更加精确的知道其类型。literal narrow 即字面量收窄(缩小范围)。
let string1 = "HelloWorld1"; // 变量
const string2 = "HelloWorld2"; // 字面量
type HelloWorld1 = typeof string1; // 'HelloWorld'
type HelloWorld2 = typeof string2; // string
枚举可以方便地当做字典使用,因为在编译为 JavaScript 之后,枚举转换为了一个同名的对象。若枚举值是数字,那么还可以用作 reflect,利用 value 来获取 key。
enum Animal {
Dog,
Cat,
}
console.log(Animal.Dog); // 0
console.log(Animal[1]); // 'Cat'
枚举值为数字时,编译为 JavaScript 之后的结果如下,可以看出额外做了 Animal[0] = 'Dog'
和 Animal[1] = 'Cat'
的赋值操作。
"use strict";
var Animal;
(function (Animal) {
Animal[Animal["Dog"] = 0] = "Dog";
Animal[Animal["Cat"] = 1] = "Cat";
})(Animal || (Animal = {}));
利用与 enum 同名的 namespace 我们可以在枚举上绑定一些方法。
enum Animal {
Dog,
Cat,
}
namespace Animal {
export function isCat(animal: Animal): animal is Animal.Cat {
return animal === Animal.Cat;
}
export function isDog(animal: Animal): animal is Animal.Dog {
return animal === Animal.Dog;
}
}
const animal = { name: 'name', type: 1 };
在通常情况下,我们使用枚举的时候需要先判断枚举值,然后再做相应的操作。
if(animal.type === Animal.Cat) {
// do something with cat
}
if(animal.type === Animal.Dog) {
// do something with dog
}
但是使用 namespace 后,我们可以直接调用枚举值对应的方法,这样代码更加清晰。
if(Animal.isCat(animal.type)) {
// do something with cat
}
if(Animal.isDog(animal.type)) {
// do something with dog
}
带有 namespace 的 enum 编译为 JavaScript 之后的结果如下。可以看出额外给 Animal 变量上挂载了 isCat 和 isDog 两个方法。
"use strict";
var Animal;
(function (Animal) {
Animal[Animal["Dog"] = 0] = "Dog";
Animal[Animal["Cat"] = 1] = "Cat";
})(Animal || (Animal = {}));
(function (Animal) {
function isCat(animal) {
return animal === Animal.Cat;
}
Animal.isCat = isCat;
function isDog(animal) {
return animal === Animal.Dog;
}
Animal.isDog = isDog;
})(Animal || (Animal = {}));
当我们声明一个 class 时,其实同时声明了两个类型,一个代表着实例的类型(class),一个代表着构造函数的类型(typeof class)。
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
const greeter1 = new Greeter('TypeScript');
type Greeter1 = typeof greeter1; // Greeter
const greeter2 = Greeter;
type Greeter2 = typeof greeter2; // typeof Greeter
使用 typeof 可以获得变量的类型。typeof 不能直接跟类型,否则报错。
const arr = [1, 'abc'];
type ArrType = typeof arr; // (string | number)[]
type tuple = [1, 'abc'];
type TupleType = typeof tuple; // Error: 'tuple' only refers to a type, but is being used as a value here.
使用 keyof 可以获取到由 interface/type 中的 key 组成的联合类型。
interface Interface {
a: number;
b: string;
c: boolean;
}
type Type = {
a: number;
b: string;
c: boolean;
}
type KeyType1 = keyof Interface; // 'a' | 'b' | 'c'
type KeyType2 = keyof type; // 'a' | 'b' | 'c'
获取一个数组中元素的类型可以使用 typeof, 获取 tuple/array 类型中的每一项的类型可以用类似数组索引的方式。
const array = [1, 'abc'];
type ArrayType = typeof arr; // (string | number)[]
type ItemTypeInArray = ArrayType[number]; // string | number
type tuple = [1, 'abc'];
type ItemTypeInTuple = tuple[number]; // 1 | 'abc'
当索引签名的 key 是 string 时,keyof 获取到的类型是 string | number
,但是当 key 是 number 的时候,keyof 获取到的类型是 number
。
interface InterfaceWithStringKey {
[key: string]: any;
}
interface InterfaceWithNumberKey {
[key: number]: any;
}
type KeyType1 = keyof InterfaceWithStringKey; // string | number;
type KeyType2 = keyof InterfaceWithNumberKey; // number;