Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

探索 TypeScript 类型注解 - 数据类型 #8

Open
6 tasks done
zhilidali opened this issue Nov 25, 2019 · 0 comments
Open
6 tasks done

探索 TypeScript 类型注解 - 数据类型 #8

zhilidali opened this issue Nov 25, 2019 · 0 comments
Assignees
Labels
ExploringTS Exploring TypeScript TypeScript JavaScript that scales

Comments

@zhilidali
Copy link
Member

zhilidali commented Nov 25, 2019

Exploring TypeScript Type Annotations - Data Types

本文由 WowBar 团队首发于 GitHub

作者: zhilidali

欢迎来到 《探索 TypeScript 类型注解》 系列教程。
开篇我们(重新)认识了 TypeScript,本篇探索 TypeScript 的原生数据类型。

目录

  1. 初识类型注解
  2. 基础类型
  3. 引用类型

1 初识类型注解

let bar: boolean = 'TS'; // Error: Type 'string' is not assignable to type 'boolean'.ts(2322)

Type Annotation 类型注解

  • 语法: Type。如 : boolean
  • 作用 :约定变量的数据类型。如约定 bar 为布尔值。
  • 好处
    1. 静态编译时检查类型。类型操作不合理时,编译器会警告,如: Type 'string' is not assignable to type 'boolean'
    2. 作为文档。在 VSCode 中将光标移到 bar 上时,会提示 let bar: boolean

2 基础类型

Primitive Type

  • 支持 JS 基础类型 : booleannumberstringsymbolundefined null
  • 新增基础类型 : voidnever
  • 新增类型 : anyunknow

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 兼容性

undefinednull 为子类型,可赋给其它类型。在 tsconfig.json 中:

  • strictNullChecks 为 false 时,undefined、null 可赋值给除 never 外的任意类型的变量。
  • strictNullChecks 为 true 时,undefined 只能赋值给 voidany 类型的变量。
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
undefinedvoid 的子类型。

  • 在 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 支持基本包装类型 BooleanNumberStringSymbol。基本类型是相应的基本包装类型的子类型。

// 基本包装类型 Boolean
let tsBool1: Boolean = new Boolean();
// 基本类型为基本包装类型的子类型
let tsBool2: Boolean = false;

3 引用类型

Reference Type

  • 支持 Object、Array、DateRegExpError、Function、Class 等类型
  • 新增 tupleenum 类型

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

三种定义方式(后两种详解见下篇):

  1. 类型后加 [],即 T[]
  2. Array Generic 数组泛型Array<T> ReadonlyArray<T>
  3. 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

枚举: 定义一组命名常量(枚举成员只读)

  1. 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
  1. String enums 字符串枚举
enum StringEnum { No = 'NO', Yes = 'YES' }
/*
var StringEnum;
(function (StringEnum) {
	StringEnum["No"] = "NO";
	StringEnum["Yes"] = "YES";
})(StringEnum || (StringEnum = {}));
*/
  1. 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 的原生数据类型,下一篇介绍如何自定义类型。

协议

CC BY-NC-ND 4.0

本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。

《探索 TypeScript 类型注解》

  1. 扩展的 JavaScript
  2. 数据类型
  3. 自定义类型
  4. 类型检查
  5. 高级类型
  6. 类型编程

参考链接

Handbook: https://github.com/microsoft/TypeScript-Handbook

@zhilidali zhilidali added TypeScript JavaScript that scales ExploringTS Exploring TypeScript labels Nov 25, 2019
@zhilidali zhilidali changed the title 探索 TypeScript 类型注解 - 内置类型 探索 TypeScript 类型注解 - 数据类型 Dec 5, 2019
@zhilidali zhilidali self-assigned this Jan 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ExploringTS Exploring TypeScript TypeScript JavaScript that scales
Projects
None yet
Development

No branches or pull requests

1 participant