探索 TypeScript 類型註解 - 數據類型

Exploring TypeScript Type Annotations - Data Typesgit

本文由 WowBar 團隊首發於 GitHubgithub

做者: zhilidali

歡迎來到 《探索 TypeScript 類型註解》 系列教程。
開篇咱們(從新)認識了 TypeScript,本篇探索 TypeScript 的原生數據類型。json

目錄

  1. 初識類型註解
  2. 基礎類型數組

  3. 引用類型函數

1 初識類型註解

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

Type Annotation 類型註解ui

  • 語法: Type。如 : boolean
  • 做用 :約定變量的數據類型。如約定 bar 爲布爾值。
  • 好處this

    1. 靜態編譯時檢查類型。類型操做不合理時,編譯器會警告,如: Type 'string' is not assignable to type 'boolean'
    2. 做爲文檔。在 VSCode 中將光標移到 bar 上時,會提示 let bar: boolean

2 基礎類型

Primitive Typespa

  • 支持 JS 基礎類型 : booleannumberstringsymbolundefined null
  • 新增基礎類型 : voidnever
  • 新增類型 : any

JS 基礎類型

TypeScript 支持 JavaScript 的基礎類型rest

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 還支持字面量類型,變量只能初始化爲相應的字面量類型值。code

// 字符串字面量
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; // 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
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; // 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 支持基本包裝類型 BooleanNumberStringSymbol。基本類型是相應的基本包裝類型的子類型。
// 基本包裝類型 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、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 國際許可協議進行許可。

相關文章
相關標籤/搜索