-
Notifications
You must be signed in to change notification settings - Fork 0
Description
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
、unknow
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; // Error,当 strictNullChecks 为 true 时
let tsVoid: void = undefined; // Ok
void 兼容性
void
作为返回值类型时,可用其它类型替换。
let foo = function (): void { };
let bar = function (): number { return 1; }
foo = bar; // Ok,foo 的类型兼容 bar 的类型
bar = 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; // OK,never 类型可赋值给 number 类型
tsNum = tsNever2; // Error,当`strictNullChecks` 为 true 时
any 兼容性
TS 还增加了 any
类型,当不希望 TS 检查时使用。
- 任意类型可以赋值给
any
类型。 any
类型可以赋值给其它类型 (never
除外) 。
let tsAny: any = 'any value';
let tsNum: number = tsAny; // Ok,any 类型可以赋值给其它类型
tsAny = true; // Ok,任意类型可以赋值给 any 类型
任意类型也可以赋值给
Object
类型 (一切皆对象),但会对其进行类型检查
let foo: any = 1;
let bar: Object = 1;
foo.toFixed(); // OK,不会进行类型检查
bar.toFixed(); // Error
unknow 兼容性
TS 增加的 unknow
相对于 any
类型更安全。
- 任意类型可以赋值给
unknown
类型。 unknown
类型不可以赋值给其它类型
let unKnow: unknown;
unKnow = 1; // Ok
unKnow = 'TS'; // Ok
unKnow.length; // Error
let foo: string = unKnow; // Error
基本包装对象兼容性
TS 支持基本包装类型
Boolean
、Number
、String
、Symbol
。基本类型是相应的基本包装类型的子类型。
// 基本包装类型 Boolean
let tsBool1: Boolean = new Boolean();
// 基本类型为基本包装类型的子类型
let tsBool2: Boolean = false;
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 国际许可协议进行许可。
《探索 TypeScript 类型注解》
- To pick up a draggable item, press the space bar. While dragging, use the arrow keys to move the item. Press space again to drop the item in its new position, or press escape to cancel.
Activity
[-]探索 TypeScript 类型注解 - 内置类型[/-][+]探索 TypeScript 类型注解 - 数据类型[/+]