Exploring TypeScript Type Annotations - Data Types
作者: zhilidali
欢迎来到 《探索 TypeScript 类型注解》 系列教程。
开篇我们(重新)认识了 TypeScript,本篇探索 TypeScript 的原生数据类型。
目录
1 初识类型注解
let bar: boolean = 'TS'; // Error: Type 'string' is not assignable to type 'boolean'.ts(2322)
Type Annotation 类型注解
-
语法 :
: Type
。如: boolean
。 -
作用 :约定变量的数据类型。如约定
bar
为布尔值。 -
好处 :
- 静态编译时检查类型。类型操作不合理时,编译器会警告,如:
Type 'string' is not assignable to type 'boolean'
。 - 作为文档。在 VSCode 中将光标移到
bar
上时,会提示let bar: boolean
。
- 静态编译时检查类型。类型操作不合理时,编译器会警告,如:
2 基础类型
Primitive Type
- 支持 JS 基础类型 :
boolean
、number
、string
、symbol
、undefined
null
- 新增基础类型 :
void
、never
- 新增类型 :
any
JS 基础类型
TypeScript 支持 JavaScript 的基础类型
let tsBoo: boolean = false;
let tsNum: number = 0x10;
let tsStr: string = 'TS';
let tsSym: symbol = Symbol('TS');
let tsUnInit: undefined;
let tsEmpty: null = { foo: 'Foo' }; // Error
TypeScript 还支持字面量类型,变量只能初始化为相应的字面量类型值。
// 字符串字面量
let ts: 'TS';
// 数值字面量
let one: 1 = 'TS'; // Error
// 布尔字面量
let truthy: true;
新增基础类型
void
void
标识函数没有返回值(即返回 undefined)。
function tsVoid(): void {
// 没有返回值
}
never
never
标识函数不会 return。如抛出异常或生成器函数存在 while(true){}
。
function tsNever1(): never {
throw new Error('Throw Exception or never return');
}
function *tsNever2(): never {
while(true) {
// ...
}
}
基础类型兼容
undefined && null
undefined
、null
为子类型,可赋给其它类型。在 tsconfig.json 中:
-
strictNullChecks
为 false 时,undefined、null 可赋值给除never
外的任意类型的变量。 -
strictNullChecks
为 true 时,undefined 只能赋值给void
、any
类型的变量。
let tsNum: number = undefined; // strictNullChecks 为 true 时,Error
let tsVoid: void = undefined; // Ok
void
void
作为返回值类型时,可用其它类型替换。
let foo = function (): void { };
let bar = function (): number { return 1; }
let baz = function (): string { return 'TS'; }
foo = bar; // Ok,foo 的类型兼容 bar 的类型
foo = baz; // Ok,foo 的类型兼容 baz 的类型
bar = foo; // Error
baz = foo; // Error
void VS. undefined
undefined
是void
的子类型。
- 在 JS 中,void 为操作符,总返回 undefined; 而 undefined 在宽松模式下,可作变量。
- 在 TS 中,
void
作为返回值类型时,可用其他类型替换,而undefined
不行。
never
never
是所有类型的子类型, 可赋给任意类型。在 tsconfig.json 中:
-
strictNullChecks
为 false 时,可直接赋给任意类型。 -
strictNullChecks
为 true 时,never
需在赋值后才能使用。
let foo = function (): never { throw 'never' }
// number 类型
let tsNum: number;
// never 类型
let tsNever1 = foo();
let tsNever2: never;
tsNum = tsNever1; // never 类型可赋值给 number 类型
tsNum = tsNever2; // `strictNullChecks` 为 true 时,提示 Error。
any
TS 还增加了 any
类型,当不希望 TS 检查时使用。
- 任意类型可以赋值给
any
类型。 -
any
类型可以赋值给其它类型 (never
除外) 。
let tsAny: any = 'any value';
let tsNum: number = tsAny; // any 类型可以赋值给其它
tsAny = true; // 任意类型可以赋值给 any 类型
任意类型也可以赋值给 Object
类型 (一切皆对象),但会对其进行类型检查
let foo: any = 1;
let bar: Object = 1;
foo.toFixed(); // 不会进行类型检查
bar.toFixed(); // Error
TS 支持基本包装类型Boolean
、Number
、String
、Symbol
。基本类型是相应的基本包装类型的子类型。
// 基本包装类型 Boolean
let tsBool1: Boolean = new Boolean();
// 基本类型为基本包装类型的子类型
let tsBool2: Boolean = false;
字面量兼容性
let foo: "foo";
let bar = "foo"; // let bar: string
foo = bar; // Error, 'foo' 不兼容 string
3 引用类型
Reference Type
- 支持 Object、Array、
Date
、RegExp
、Error
、Function、Class 等类型 - 新增
tuple
、enum
类型
Object
{ prop: T }
描述对象类型的结构 (详解见下篇)
let obj: { a: number } = { a: 1 };
obj.a = 2; // OK
obj.b; // Error
注:object
描述的对象类型类似于{}
let obj1: {} = { a: 1 };
let obj2: object = { a: 1 };
obj1.a; // Error: Property 'a' does not exist on type '{}'
obj2.a; // Error: Property 'a' does not exist on type 'object'
Array
三种定义方式(后两种详解见下篇):
- _类型后加
[]
_,即T[]
-
Array Generic 数组泛型 :
Array<T>
ReadonlyArray<T>
-
Number Index Signature 数值索引签名 : 通过
interface
定义类型
let tsArr1: string[] = ['foo'];
// 数组泛型
let tsArr2: Array<string> = ['foo'];
let readonlyArr: ReadonlyArray<string> = ['foo']; // 只读数组
// 数值索引签名
interface IdxType { [index: number]: string }
let tsArr3: IdxType = ['foo'];
Tuple
元组:已知固定元素及类型的数组
let tsTuple: [string, number] = ['foo', 1];
tsTuple[0] = 1; // Error
tsTuple[2]; // Error,访问索引之外的元素时会显示错误
Enum
枚举: 定义一组命名常量(枚举成员只读)
- Numeric enums 数值枚举 : 可定义初始值(默认为 0),后面根据初始值递增。
enum NumericEnum { Foo = 2, Bar, Baz };
/* 编译器反向映射为
var NumericEnum;
(function (NumericEnum) {
NumericEnum[NumericEnum["Foo"] = 2] = "Foo";
NumericEnum[NumericEnum["Bar"] = 3] = "Bar";
NumericEnum[NumericEnum["Baz"] = 4] = "Baz";
})(NumericEnum || (NumericEnum = {}));
*/
let num: NumericEnum = NumericEnum.Bar; // 3
let str: string = NumericEnum[3]; // Bar
- String enums 字符串枚举
enum StringEnum { No = 'NO', Yes = 'YES' }
/*
var StringEnum;
(function (StringEnum) {
StringEnum["No"] = "NO";
StringEnum["Yes"] = "YES";
})(StringEnum || (StringEnum = {}));
*/
- Heterogeneous enums 异构枚举
enum HeterogeneousEnum { No = 0, Yes = "YES" }
/*
var HeterogeneousEnum;
(function (HeterogeneousEnum) {
HeterogeneousEnum[HeterogeneousEnum["No"] = 0] = "No";
HeterogeneousEnum["Yes"] = "YES";
})(HeterogeneousEnum || (HeterogeneousEnum = {}));
*/
Date RegExp Error
let date: Date = new Date();
let reg: RegExp = /\.ts$/;
let err: Error = new Error('error');
Function
描述参数和返回值类型
-
(paramter: T): U
描述函数定义 -
: (paramter: T) => U
描述函数变量
// 定义函数声明
function fn1(s: string): string {
return s;
}
// 定义箭头函数表达式
let fn2 = (s: string): string => s;
// 函数变量 fn
let fn3: (str: string) => string = fn2;
参数
-
required parameters 必选参数 :
foo: T
-
optional parameters 可选参数 :
foo?: T
-
default parameters 默认参数 :
foo: T = value
-
rest parameter 剩余参数 :
...rest: T[]
let fn = (
s: string, // 必选参数
b: string = '', // 默认参数
c?: string, // 可选参数;位于必选参数后面
...d: string[] // 剩余参数;位于参数最后
): string => s;
重载
// Overload
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
Class
TypeScript 保留了 ES6 的语法,以下是分别用 ES6 和 TS 语法实现的类的创建和继承:
// ES6 语法
class A {
constructor(msg) {
this.foo = msg;
}
getFoo() {
return this.foo;
}
}
class B extends A {
constructor(msg) {
super(msg);
}
getFoo() {
return 'b' + super.getFoo();
}
}
// TS 语法
class A {
foo: string
constructor(msg: string) {
this.foo = msg;
}
getFoo() {
return this.foo;
}
}
class B extends A {
constructor(msg: string) {
super(msg);
}
getFoo() {
return 'b' + super.getFoo();
}
}
TS 在 ES6 基础上对 class 增添了功能。
三种访问修饰符:
-
public
在 TS 中,成员默认为公有成员。 -
private
私有成员只能在类中访问,不能在类的外部访问。 -
protected
受保护的成员只能在类和子类中访问。
class A {
public foo: string; // foo: string;
private bar: string;
protected baz: string;
constructor(msg: string) {
this.foo = msg;
this.bar = msg;
this.baz = msg;
}
getFoo() {
return this.foo;
}
}
class B extends A {
constructor(msg: string) {
super(msg);
this.foo; // Ok
this.bar; // Error: Property 'bar' is private and only accessible within class 'A'.
this.baz; // Ok
}
getFoo() {
return super.getFoo() + 'b';
}
}
const b = new B('str');
b.foo; // Ok
b.bar; // Error: Property 'bar' is private and only accessible within class 'A'.
b.baz; // Error: Property 'baz' is protected and only accessible within class 'A' and its subclasses
抽象类: 使用 abstract
定义抽象类和抽象类中的抽象方法
- 抽象类不允许被实例化。
- 抽象类中的抽象方法不包含具体实现且必须被在派生类中实现。
abstract class A {
foo: string;
constructor(msg: string) {
this.foo = msg;
}
// 抽象方法不包含具体实现
abstract getFoo(): string;
}
class B extends A {
// 必须被在派生类中实现抽象方法
getFoo() {
return this.foo;
}
}
结语
本篇介绍了 TS 的原生数据类型,下一篇介绍如何自定义类型。
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。