typeScript-入门学习02篇

高级使用

联合类型

表示一个值可以是几种类型之一,适合于那些值可以为不同类型的情况。我们用竖线( | )分隔每个类型,所以 number | string | boolean表示一个值可以是 number, string,或 boolean。当函数的入参或者某个变量,可以是多个不同类型时使用。但是需要注意的是,联合类型的值只能调用所有类型里共有的方法或属性

1
2
3
4
function padLeft(value: string, padding: string | number) {
// ...
}
let a = string | number

交叉类型

将多个类型合并为一个类型,使用 & 符号将多个类型组合在一起,表示结果的类型是所有列出类型的集合。

1
2
3
4
5
6
7
8
9
10
11
12
type Student = {
id: string;
age: number;
};
type Employee = {
companyId: string;
};
let person: Student & Employee;
person.age = 21; // ✅
person.companyId = 'SP302334'; // ✅
person.id = '10033402'; // ✅
person.name = 'Henry'; // ❌ - name does not exist in Student & Employee

类型保护与区分类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface Bird {
fly();
layEggs();
}

interface Fish {
swim();
layEggs();
}

function getSmallPet(): Fish | Bird {
// ...
}

let pet = getSmallPet();

联合类型适合于那些值可以为不同类型的情况。 但当我们想确切地了解是否为 Fish时怎么办?
方法1:
通常使用类型断言,确保这段代码不会报错

1
2
3
4
5
6
7
8
let pet = getSmallPet();

if ((<Fish>pet).swim) {
(<Fish>pet).swim();
}
else {
(<Bird>pet).fly();
}

方法2:
但是对于我们判断过的类型,在使用时还要再次判断,就感觉是冗余的麻烦的,所以我们会寻求更好的方法: 用户自定义的类型保护
我们想要的效果是,一次检查类型后续直接知道类型而不需要再检查,就要使用TypeScript里的类型保护机制

谓词为 parameterName is Type这种形式,parameterName必须是来自于当前函数签名里的一个参数名。

1
2
3
function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined;
}

每当使用一些变量调用 isFish时,TypeScript会将变量缩减为那个具体的类型,只要这个类型与变量的原始类型是兼容的。

注意TypeScript不仅知道在 if分支里 pet是 Fish类型; 它还清楚在 else分支里,一定 不是 Fish类型,一定是 Bird类型。

1
2
3
4
5
6
7
8
// 'swim' 和 'fly' 调用都没有问题了

if (isFish(pet)) {
pet.swim();
}
else {
pet.fly();
}

typeof类型保护

1
2
3
function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined;
}

不是必须要像上面一样,定义一个函数来判断类型是否是原始类型,也就是不必将 typeof x === “number”抽象成一个函数,因为TypeScript可以将它识别为一个类型保护
可以直接这样

1
2
3
4
5
6
7
8
9
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}

这些* typeof类型保护*只有两种形式能被识别: typeof v === “typename”和 typeof v !== “typename”, “typename”必须是 “number”, “string”, “boolean”或 “symbol”。 但是TypeScript并不会阻止你与其它字符串比较,语言不会把那些表达式识别为类型保护。

instanceof类型保护

instanceof类型保护是通过构造函数来细化类型的一种方式。
instanceof的右侧要求是一个构造函数,TypeScript将细化为:
1、此构造函数的 prototype属性的类型,如果它的类型不为 any的话
2、构造签名所返回的类型的联合

1
2
3
4
5
6
7
8
// 类型为SpaceRepeatingPadder | StringPadder
let padder: Padder = getRandomPadder();
if (padder instanceof SpaceRepeatingPadder) {
padder; // 类型细化为'SpaceRepeatingPadder'
}
if (padder instanceof StringPadder) {
padder; // 类型细化为'StringPadder'
}

类型保护和类型断言

类型断言可以手动去除null。语法是添加 ! 后缀: identifier!从 identifier的类型里去除了 null和 undefined, 这对很多需要调用该变量类型的方法时很有用,起到了类型保护的容错作用、保证代码的健壮性,自己不用先判断该值是否存在再赋值调用了:

1
2
3
4
5
6
7
8
function fixed(name: string | null): string {
function postfix(epithet: string) {
// return name.charAt(0) + '. the ' + epithet; // error, 'name' is possibly null
return name!.charAt(0) + '. the ' + epithet; // ok
}
name = name || "Bob";
return postfix("great");
}

类型别名

类型别名会给一个类型起个新名字。 类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。起别名不会新建一个类型 - 它创建了一个新名字来引用那个类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
}
else {
return n();
}
}
type Container<T> = { value: T };
type Tree<T> = {
value: T;
left: Tree<T>;
right: Tree<T>;
}

接口 vs. 类型别名

1.错误信息不会使用别名。
2.接口是创建一个新的类型,别名不会创建一个新类型,是对原有类型的引用。
3.即使使用别名,编辑器只能提示还是会显示原有类型名称。

索引类型

1
2
3
4
5
6
7
8
9
10
11
12
13
function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
return names.map(n => o[n]);
}

interface Person {
name: string;
age: number;
}
let person: Person = {
name: 'Jarid',
age: 35
};
let strings: string[] = pluck(person, ['name']); // ok, string[]

这段代码中,keyof T索引类型查询操作符**T[K], 索引访问操作符

对于任何类型 T, keyof T的结果为 T上已知的公共属性名的联合

let personProps: keyof Person; // 'name' | 'age'

使用 const enum 维护常量表

1
2
3
4
5
6
7
8
9
10
// 使用 const enum 维护常量
const enum TODO_STATUS {
TODO = 'TODO',
DONE = 'DONE',
DOING = 'DOING'
}

function todos (status: TODO_STATUS): Todo[];

todos(TODO_STATUS.TODO)

typescript 工具函数

学习了这些属性,感觉自己对TypeScript有了入门的一些了解,其余暂未能学习了解到内容,就等着在工作中踩坑学习了。在工作中,引入TypeScript确实是很强大很方便的一个选择,方便我们维护代码的同时,极重要的增强了代码的健壮性,类似can not find property、没有这个方法等等的错误,编译时就能让你意识到去避免,使得线上的代码更可靠!