前言:根据资料类型来学习
- (已讲完)原始资料型别(Primitive types) : string (字串)、number (数值)、boolean (布林值)、null、undefined
- string (字串)、number (数值)、boolean (布林值)没什么好讲的,跟JavaScript一样~~
null、undefined可以看juejin.cn/post/709521…
- (这期讲)TypeScript 才有的型别: any、unknown、void、 never、 union types (联合型别) 、intersection types(交集型别)、 literal types (字面值型别)、 tuple (元组)、 enums (列举)
-
any、unknown、 never、可以看juejin.cn/post/709521… -
讲一下剩下的union types (联合型别) 、intersection types(交集型别)、 literal types (字面值型别)、 tuple (元组)、 enums (列举)
- (下期讲)物件型别(Object types): object (物件) 、 arrays (阵列) 、function (函式)
union types=‘或’,intersection types=‘与’
function printId2(id: number | string) {
if (typeof id === "string") {
//型别为字串才 toUpperCase
console.log(id.toUpperCase());
} else {
//其他自动判定为 number 型别
console.log(id);
}
}
printId2("ABC");
union types实际开发中多与narrowing一起使用,下面讲一下narrowing.....
union types好朋友:narrowing把多个型别缩小为一个
1.使用 typeof型别保护
typeof 的操作可以让我们取得值的型别类型,如string, number, boolean, symbol, undefined, object, function。但 null 除外。
如下例子,typeof strs === "object"这就是一种 type guards (型别保护)。虽然 null 无法使用 typeof 来取得型别,但 null 也是 object, TypeScript 会提醒 strs 有可能是 null。(tsconfig 记得打开严谨模式)
function printAll(strs: string | string[] | null) {
if (typeof strs === "object") {
for (const s of strs) {
//Object is possibly 'null'.
}
} else if (typeof strs === "string") {
console.log(strs);
} else {
console.log(strs);
}
}
2.使用 switch 或是===,!==,==, and!=来缩小型别
function printAll(strs: string | string[] | null) {
if (strs !== null) {
if (typeof strs === "object") {
for (const s of strs) {
console.log(s);
}
} else if (typeof strs === "string") {
console.log(strs);
}
}
}
3.使用"value" in x, value 为 string literal,x 为联合型别
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(animal: Fish | Bird) {
if ("swim" in animal) { //animal 物件里有属性 "swim",则一定是 Fish 型别
return animal.swim();
}
return animal.fly();
}
4.使用instanceof检查某个值是否为某个 constructor
function logValue(x: Date | string) {
if (x instanceof Date) {
console.log(x.toUTCString());
//(parameter) x: Date
} else {
console.log(x.toUpperCase());
//(parameter) x: string
}
}
5.使用 Assignments
let x = Math.random() < 0.5 ? 10 : "hello world!";
console.log(Math.random());
console.log(x);
x = 1;
console.log(x); //1
x = true; //error:Type 'boolean' is not assignable to type 'string | number'.
6. 定义一个函式,这个函式返回的值就是 type predicates
- 函式最后会回传 true 或 false,如果是 true,表示符合该变数符合该 type predicate。
type Fish = { swim: () => void };
type Bird = { fly: () => void };
//pet 有可能是 Fish or Bird 的型别
//isFish 这个function 回传值的型别为 : pet is Fish,看是回传 true or false
//当返回了true, 那他就是Fish
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined; //(pet as Fish)断言 pet 为 Fish 型别 , 所以 swim 不会是undefined
}
7.Discriminated unions (可辨识联合)
TypeScript 可以针对相同的可识别的属性来自动判断型别,再去执行不同情境的操作,他有 3 个要素:
- 每个 interface 需要有相同的可辨识的属性,如
status - 使用 type alias 宣告联合型别,他包含了哪些型别, 如
type Resp = ISuccessResp | IErrorResp; - 使用 type guard, 如
resp.status === true
用例:可用在后端回传 response
-
status 是 OK 的话,则资料会带有 payload,否则资料会带有 error。我们可以自行定义多个不同的 Type,这些 Type 有共同的栏位, 如
status,那就用这个栏位来判断还有哪些属性。 -
最后没有判断到的情况可以利用 exhaustiveness checking, 用
never型别表示不应该存在的状态,一般用于错误处理。
interface ISuccessResp {
status: 'OK'; //相同栏位 status
payload: unknown;
}
interface IErrorResp {
status: 'ERROR';
errorCode: number;
description: string;
}
type Resp = ISuccessResp | IErrorResp;
const parseResponse = (resp: Resp) => {
switch (resp.status) {
case 'OK': //透过 narrow 知道是 ISuccessResp 型别
return resp.payload; //则去使用ISuccessResp的属性payload
case 'ERROR': // 透过 narrow 知道是 IErrorResp 型别
return resp.description; //则去使用IErrorResp的属性payload
default:
const _exhaustiveCheck: never = resp;
return _exhaustiveCheck;
}
};
Literal Types 字面值型别: 值当作 "型别" 来使用
Literal Types 字面值型别: "值" 可以当作 "型别" 来使用,用来约束取值只能是某几个中的一个。
function printText(s: string, alignment: "left" | "right" | "center") {
console.log(`${s} placed at the ${alignment}`)
}
printText("Hello, world", "left");
function compare(a: string, b: string): -1 | 0 | 1 {
return a === b ? 0 : a > b ? 1 : -1;
}
//String Literal Type
type Index = 'a' | 'b' | 'c';
// Mapped Types
type FromIndex = {
[k in Index]?: number
};
const bad: FromIndex = {
b: 1,
c: 2,
d: 3
//Object literal may only specify known properties, and 'd' does not exist in type 'FromIndex'.
};
列举(Enums)型别用于取值被限定在一定范围内
列举(Enums)型别用于取值被限定在一定范围内的场景,可以用来管理多个同系列的常数,作为状态判断使用。比如一周只能有七天,颜色限定为红绿蓝等。也可以自定义来表示每一个值。
列举项有两种型别:常数项(constant member)和计算所得项(computed member)
- 像是二元运算子
<<、 |、&等都归为常数项,常数项会在编译阶段被删除,并且不能包含 computed member (计算成员), 只能是常数项(constant member)。
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
- 其他则为计算所得项,
"blue".length就是一个计算所得项。:
enum Color { Red, Green, Blue = "blue".length };
console.log(Color.Blue); //4
-
外部列举(Ambient Enums)是使用 declare enum 定义的列举型别。declare 定义的型别只会用于编译时的检查,编译结果中会被删除。
declare enum Directions {
Up,
Down,
Left,
Right
}
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
// var directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
Tuple 就是合并了不同型别的物件
//直接对元组型别的变数进行初始化或者赋值,需提供所有项目。如果要新增不同型别的项目也是无法的:
let tom: [string, number];
tom = ['tom', 18]; //如果只有宣告tom沒賦值,會是undefined,tsconfig strictNullChecks 打開的話會報錯提醒
tom[0] = 'Tom'; //ok
tom[1] = 25; //ok
tom[0].slice(1); //ok
tom[1].toFixed(2); //ok
tom.push('male'); //ok