写点什么

从 0 开始的 TypeScriptの三:TS 的类型

用户头像
空城机
关注
发布于: 2 小时前
从0开始的TypeScriptの三:TS的类型

上文《从0开始的TypeScriptの二:类型系统》中说过,JavaScript 有的基础类型在TypeScript中都存在。


本次来介绍一些TypeScript中新的类型, 有元组,枚举,任意值,Unknown,空值,Never


TypeScript对于类型系统的使用,还存在类型别名和类型断言等用法。

元组 Tuple

之前写数组的过程中,肯定会想,如果我不用后缀类型注释,是不是就可以写不同类型组成的数组了。


但这在 TypeScript 中如此定义,就是另一个数据类型了,叫做元组 Tuple


元组是 JavaScript 当中不存在的数据类型


元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同


TS:

let tuple1 = ['10', 10];let tuple2: [string, number] = ['10', 10];let tuple3: [string, number, number] = ['10', 10, '5'];  //Type 'string' is not assignable to type 'number'
复制代码


JS:

var tuple1 = ['10', 10];var tuple2 = ['10', 10];var tuple3 = ['10', 10, '5'];  
复制代码


从上面的例子来看,元组就相当于一个长度,每一项元素类型都确定的数组


使用后缀类型注释时确定好的格式如果在赋值时不符,编译时会出现报错


枚举 enum

枚举类型同样是 JavaScript 中没有的


枚举类型用于取值被限定在一定范围内的场景。比如设定日期为周一到周日


TS:

// 使用枚举限定日期enum day { Mon, Tue, Wed, Thu, Fri, Sat, Sun}
复制代码


JS:

// 使用枚举限定日期var day;(function (day) {    day[day["Mon"] = 0] = "Mon";    day[day["Tue"] = 1] = "Tue";    day[day["Wed"] = 2] = "Wed";    day[day["Thu"] = 3] = "Thu";    day[day["Fri"] = 4] = "Fri";    day[day["Sat"] = 5] = "Sat";    day[day["Sun"] = 6] = "Sun";})(day || (day = {}));
复制代码


使用 node 运行 js 文件来打印 day

自动赋值

枚举成员会被自动赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射

console.log(day[0]);   // Monconsole.log(day['Fri']);   // 4  反向映射
复制代码

手动赋值

枚举成员也可以进行手动赋值

enum day { Mon = 1, Tue = 7, Wed, Thu, Fri, Sat, Sun}
复制代码


这样一来,编译后的结果(成员后面没有手动赋值的,会从当前成员的值开始递增):


var day;(function (day) {    day[day["Mon"] = 1] = "Mon";    day[day["Tue"] = 7] = "Tue";    day[day["Wed"] = 8] = "Wed";    day[day["Thu"] = 9] = "Thu";    day[day["Fri"] = 10] = "Fri";    day[day["Sat"] = 11] = "Sat";    day[day["Sun"] = 12] = "Sun";})(day || (day = {}));
复制代码


注意: 在手动赋值时,有可能会造成重复的情况


enum day { Mon = 3, Tue = 2, Wed, Thu, Fri = 3, Sat, Sun}
复制代码


上面的例子中,Mon 的值会与 Wed 和 Fri 值重复,都为 3。 Thu 和 Sat 的值都为 4.


所以最好不要出现这种覆盖的情况。


另外,手动赋值的枚举项可以不是数字,但是这需要使用类型断言让 tsc 无视类型检查


enum day { Mon = <any>'MonDay', Tue = 2, Wed, Thu, Fri = <any>'FriDay', Sat = 5, Sun = <any>'SunDay'}
复制代码

常数项和计算所得项

枚举项有两种类型:


  • 常数项(constant member)

  • 计算所得项(computed member)


之前定义的枚举值都是常数项,如果是计算所得项:


enum key { key1 = 1 + 1, key2 = 'hello'.length }
复制代码


'hello'.length 属于计算所得项, 1 + 1属于常数项。


但是这里有一个缺陷,如果在计算所得项之后是 未手动赋值的项, 那么就会因为无法活得初始值而报错


比如:enum key { key1 = 1 + 1, key2 = 'hello'.length, key3 }。会报错:error TS1061: Enum member must have initializer.


枚举项被当作常数项的条件:


  • 不具有初始化函数并且之前的枚举成员是数字常数。 在这种情况下,当前枚举成员的值为上一个枚举成员的值加 1。 第一个枚举成员除外,如果没有初始化方法,那么初始值为 0

  • 枚举成员使用常数枚举表达式初始化。常数枚举表达式是 TypeScript 表达式的子集, 可以在编译阶段求值。 当表达式满足下面条件之一时,就是一个常数枚举表达式:

  • 数字字面量

  • 引用之前定义的常数枚举成员(可以是在不同枚举类型中定义的),如果这个成员是在同一个枚举类型定义的,可以使用非限定名来引用

  • 带括号的常数枚举表达式

  • +,-,~等一元运算符应用在常数枚举表达式

  • +,-,*,/,%,<<,>>,>>>,&,|,^二元运算符,常数枚举表达式若求值后为 NaN 或 Infinity,则会在编译阶段报错,上面的 1 + 1就是在编译阶段求值

数字枚举

之前自动赋值形成的枚举就属于数字枚举


数字枚举是枚举项都是数字常量的枚举


<br>

字符串枚举

字符串枚举中,每一个枚举成员都必须使用字符串字面量或者另一个字符串枚举成员进行初始化


enum str { one = 'first', two = `second`, three = 'third' }
复制代码

常数枚举

常数枚举和上面讲到的常数项不是同一种东西


常数枚举是使用 const enum 定义的枚举类型


常数枚举会在编译阶段被删除,并且不能包含计算项


例子:TS:


// 常数枚举const enum cenum { a, b, c }
复制代码


在 tsc 编译出的 js 中没有找到 cenum 的声明,并且如果使用计算项,会出现报错error TS2474: const enum member initializers can only contain literal values and other computed enum values

外部枚举 Ambient Enums

外部枚举是使用declare enum定义的枚举类型


与常数枚举相同,外部枚举也在编译阶段被删除


因为之前提到过,declare定义的类型只会用于编译时的检查,在编译结果中会被删除


例子:TS

// 外部枚举declare enum enumFruit { apple = 'red', orange = 'yellow' }let fruit = [ enumFruit.apple, enumFruit.orange ]
复制代码


编译后:JS

var fruit = [enumFruit.apple, enumFruit.orange];
复制代码


外部枚举经常出现在声明文件中,并且可以和常数枚举同时使用 declare const enum


<br><br>

任意值 any

任意值 any 也是 JavaScript 中不存在的类型


在编程阶段,可能会遇到变量不知道会是什么类型的情况, 比如这些变量的值可能来自动态内容


这种情况下,就需要我们关闭类型检查器,不然会出现一堆类型报错。


any在 TypeScript 类型系统中又特殊地位, 它与类型系统中的任何类型都兼容。意味着可以将任何内容赋值给它,也可以将它赋值给任何类型。它能让你避开类型检查。


TS 例子:

// 任意值let num: any
num = 10;num = '111'num = falseconsole.log(typeof num);
let k: numberk = num
复制代码


运行 tsc 后,编译不会有报错。 使用typeof可以判断变量类型


当需要从 JavaScript 迁移代码到 TypeScript 时,会经常使用any。 但是必须减少对它的依赖,因为这是类型安全的要求。

Unknown

TypeScript 3.0 引入了一个顶级的unknown类型。 对照于anyunknown是类型安全的, 或者说unknownany的安全版本,在使用any前,可以先尝试使用unknown


any 允许我们做任何事的地方,unknown 的限制则大得多


当我们在写应用的时候可能会需要描述一个我们还不知道其类型的变量。这些值可以来自动态内容,例如从用户获得,或者我们想在我们的 API 中接收所有可能类型的值。在这些情况下,我们想要让编译器以及未来的用户知道这个变量可以是任意类型。这个时候我们会对它使用 unknown 类型。


unknown限制:当没有类型断言或基于控制流的类型细化时 unknown 不可以赋值给其它类型


例子:

let a: unknown = '123';let b: any = '456';
let a1: string = a; // Type 'unknown' is not assignable to type 'string'let b1: string = b;
复制代码


如果给 a 附加了类型断言,就不会出现报错问题

let a1: string = a as string;let a2: boolean = <boolean>a;
复制代码


或者使用typeof进行类型防护

if (typeof a === 'string') {    let a3: string = a;}
复制代码

空值 void

空值void类型与any相反,表示没有任何类型。


通常会用void表示一个函数没有返回值;

function login(): void {    console.log(123);}
复制代码

Never

never类型表示的是那些永不存在的值的类型


never类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。


即使 any 也不可以赋值给never


例子:

// 返回never的函数必须存在无法达到的终点function error(message: string): never {    throw new Error(message);}try {    JSON.parse('');} catch (err) {    error("出错了");}
复制代码


不能将除 never 以外的类型赋值给 never

var ok: never, aaa: any = 4;
ok = '111'; // Type '"111"' is not assignable to type 'never'.ok = aaa; // Type 'any' is not assignable to type 'never'.ok = undefined; // Type 'undefined' is not assignable to type 'never'.ok = null; // Type 'null' is not assignable to type 'never'.aaa = ok;
复制代码


类型别名 type

类型别名用来给一个类型起个新名字。


type Gender = "nan" | "nu"type str_num = string|number;
var log1:str_num = '123';var log2:str_num = 123;var log3:str_num = false; // Type 'false' is not assignable to type 'string | number'.
type user = { name: string, age: number, gender: Gender}let u: user;
复制代码

类型断言

类型断言(Type Assertion)可以用来手动指定一个值的类型。


使用语法有两种:


  1. as 类型

  2. <类型>


可以给联合类型只访问其中一个类型

type animal = Cat|Dog;interface Cat {    name: string;    run(): void;}interface Dog {    name: string;    swim(): void;}
let an1: animal;// 令an1 为 Cat(an1 as Cat).name = 'Tom'
复制代码


需要注意的是,类型断言只能够 「欺骗」 TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误

发布于: 2 小时前阅读数: 6
用户头像

空城机

关注

曾经沧海难为水,只是当时已惘然 2021.03.22 加入

业余作者,在线水文 主要干前端的活,业余会学学python 欢迎各位关注,互相学习,互相进步

评论

发布
暂无评论
从0开始的TypeScriptの三:TS的类型