你需要知道 TypeScript 的那些事<二>
写在前面:本次 ts 系列文章共4篇,从入门到"入土"系列,本文为第二篇,且本文仅作为学习笔记。
你若盛开,清风自来。想要获取更多的财富,就要让自己变得更有价值。
上回说到:TS 是 JS 的超集(包含关系);TS 中对于变量、函数接收参数和返回参数等都有严格的类型判断;TS中 type 类型别名和 interface 接口 的区别,大体上没有区别,但是在扩展、继承等方面还是有些差异;详情请查看:第一篇链接
本文内容
- 类型保护
- 类型断言 as
- 类型收窄 in
- 类型收窄 typeof
- 类型收窄 instanceof 只能用在类上
- 常用类型
- 文字类型
- 枚举类型 enmu
- never 类型
本文阅读时间大约为15分钟,请听笔者娓娓道来。
类型保护
有时候你会得到一个值的类型信息,而 typescript 是不能知道的。
// 声明两个接口Waiter(服务员)接口和Teacher(技师)接口,然后在写一个judgeWho(判断是谁)的方法,里边传入一个animal(任意值),这时候可以能是Waiter,也可能是Teacher。所以我们使用了联合类型,关键符号是|(竖线)
interface Waiter {
jiedai: boolean;
say: () => {};
}
interface Teacher {
anjiao: boolean;
skill: () => {};
}
function judgeWho(animal: Waiter | Teacher) {
animal.say(); // 这样写会报错 因为 judgeWho 不能准确的判断联合类型具体的实例是什么
}
类型断言 - as
类型断言就是通过断言的方式确定传递过来的准确值
// 这时候就需要再引出一个概念叫做类型保护
// 类型保护之类型断言:类型断言就是通过断言的方式确定传递过来的准确值
function judgeWho(animal: Waiter | Teacher) {
if (animal.anjiao) {
(animal as Teacher).skill();
}else{
(animal as Waiter).say();
}
}
与类型注释一样,类型断言由编译器移除,不会影响代码的运行时行为。
类型收窄 - in
function judgeWhoTwo(animal: Waiter | Teacher) {
if ("skill" in animal) {
animal.skill();
} else {
animal.say();
}
}
类型收窄 - typeof
function add(first: string | number, second: string | number) {
return first + second;
}
function add(first: string | number, second: string | number) {
if (typeof first === "string" || typeof second === "string") {
return `${first}${second}`;
}
return first + second;
}
类型收窄 - instanceof
// 类型保护之 instanceof 只能用在类上
class NumberObj {
count: number;
}
function addObj(first: object | NumberObj, second: object | NumberObj) {
if (first instanceof NumberObj && second instanceof NumberObj) {
return first.count + second.count;
}
return 0;
}
常用类型
目的:收录项目中常用类型,后期会做补充。
文字类型
单独的使用文字类型并不是很有价值(比鸡肋还鸡肋)。
举个🌰
// x 的类型为 hello,表示值也只能是 hello,其它就会报错
let x: "hello" = "hello";
x = "howdy";
// Type '"howdy"' is not assignable to type '"hello"'.
但是,通过将文字组合成联合,可以表达一个更有用的概念。
举个🌰
function printText(s: string, alignment: "left" | "right" | "center") {
// ...
}
printText("Hello, world", "left");
printText("G'day, mate", "centre");
// Argument of type '"centre"' is not assignable to parameter of type '"left" | "right" | "center"'.
这样就做到了参数的限制,同样也可以限制返回值。
举个🌰
function compare(a: string, b: string): -1 | 0 | 1 {
return a === b ? 0 : a > b ? 1 : -1;
}
枚举类型
枚举允许开发人员定义一组命名的常量。使用枚举可以更容易地记录意图,或者创建一组不同的案例。
举个🌰
// 定义了一个 Status 的枚举类型
enum Status {
SUCCESS,
FAIL,
WARN,
}
聪明的脑袋瓜子里是否在想,为啥要用 enum ? enum 有什么好处?
- 枚举可以让输入变得更简单
- 更容易记录意图
举个🌰
// 初级程序员写法:
function getStatus(status: number) {
if (status === 0) {
return "SUCCESS";
} else if (status === 1) {
return "FAIL";
} else if (status === 2) {
return "WARN";
}
}
const result = getStatus(0);
console.log(`状态为${result}`);
// 中级程序员写法:
const Status = {
MASSAGE: 0,
SPA: 1,
DABAOJIAN: 2,
};
function getStatus(status: any) {
if (status === Status.SUCCESS) {
return "SUCCESS";
} else if (status === Status.FAIL) {
return "FAIL";
} else if (status === Status.WARN) {
return "WARN";
}
}
const result = getStatus(Status.SUCCESS);
console.log(`状态为${result}`);
写代码的目标是,让代码不要成为屎山。接下来,咱们用枚举来改写。
举个🌰
// 高级程序员写法:
enum Status {
SUCCESS,
FAIL,
WARN,
}
function getStatus(status: any) {
if (status === Status.SUCCESS) {
return "SUCCESS";
} else if (status === Status.FAIL) {
return "FAIL";
} else if (status === Status.WARN) {
return "WARN";
}
}
// const result = getServe(Status.SUCCESS);
// console.log(`状态为${result}`);
// 改写成枚举类型对应值
const result = getServe(1);
// 这看起来很神奇,这是因为枚举类型是有对应的数字值的,默认是从 0 开始的。我们直接用console.log()就可以看出来了。
console.log(Status.SUCCESS); // 0
console.log(Status.FAIL); // 1
console.log(Status.WARN); // 2
// 那这时候不想默认从 0 开始,而是想从 1 开始。可以这样写。
enum Status {
SUCCESS = 1,
FAIL,
WARN,
}
never 类型
never 类型是 TypeScript 中的底层类型。它自然被分配的一些例子:
- 一个从来不会有返回值的函数(如:如果函数内含有
while(true) {}); - 一个总是会抛出错误的函数(如:
function foo() { throw new Error('Not Implemented') },foo的返回类型是never);
举个🌰
let foo: never; // ok
let foo: never = 123; // Error: number 类型不能赋值给 never 类型
// 作为函数返回类型的 never
let bar: never = (() => {
throw new Error('Throw my hands in the air like I just dont care');
})();
除了以上方式,还可以用作详细的检查。
举个🌰
function foo(x: string | number): boolean {
if (typeof x === 'string') {
return true;
} else if (typeof x === 'number') {
return false;
}
// 如果不是一个 never 类型,这会报错:
// - 不是所有条件都有返回值 (严格模式下)
// - 或者检查到无法访问的代码
// 但是由于 TypeScript 理解 `fail` 函数返回为 `never` 类型
// 它可以让你调用它,因为你可能会在运行时用它来做安全或者详细的检查。
return fail('Unexhaustive');
}
function fail(message: string): never {
throw new Error(message);
与 void 的差异
never 表示一个从来不会优雅的返回的函数时,你可能马上就会想到与此类似的 void,然而实际上,void 表示没有任何类型,never 表示永远不存在的值的类型。
当一个函数返回空值时,它的返回值为 void 类型,但是,当一个函数永不返回时(或者总是抛出错误),它的返回值为 never 类型。void 类型可以被赋值(在 strictNullChecking 为 false 时),但是除了 never 本身以外,其他任何类型不能赋值给 never。
总结
本文主要讲述了类型保护中 as、in、typeof、interface 的一些用法以及常用的类型如文字类型、枚举类型和 never 类型。如有不当之处,请不吝赐教。最后,笔者近期加入了 mini-vue 作者创建的 ts 学习小组,共同学习。有兴趣的话,拉你入群。之后还会有多种形式的对赌学习哦!