TypeScript 學習與總結

參考文檔:github.com/zhongsp/Typ…git

基本類型

  • boolean
  • number
  • string
  • 數組 T[]
  • 元組 [T,U]
  • 枚舉 enum
  • any
  • 空值 void 與 any 相反,表示沒有任何類型
  • null
  • undefined
  • never 表示永遠不存在的類型,如拋出異常
  • object

泛型

在像c#和Java這樣的語言中,建立 可重用組件 的工具箱中的一個主要工具是泛型,也就是說,可以建立能夠在多種類型上工做的組件,而不是單個類型。這容許用戶使用這些組件並使用本身的類型es6

泛型例子

  • 函數返回值和輸入值的類型相同
    • 不使用泛型
    function clone(a:any):any{
        return a;
    }
    複製代碼
    • 使用泛型
    function clone<T>(a:T):T{
        return a;
    }
    
    let r1=clone<string>("haha"); // r1:string
    let r2=clone<number>(1); // r2:number
    // 能夠不明確傳入類型,編譯器會自動推斷
    let r3=clone(true); // r3:boolean
    複製代碼

泛型變量

把泛型當作變量做爲類型的一部分使用github

function logger<T>(args:Array<T>):Array<T>{
    console.log(args.length);
    return args;
}
複製代碼

也能夠以下這麼寫:json

function logger<T>(args:T[]):T[]{
    console.log(args.length);
    return args;
}
複製代碼

泛型類型

  • 泛型函數類型
function clone<T>(arg: T): T {
    return arg;
}

let f1: <T>(arg: T) => T = clone;
// 也可使用帶有調用簽名的對象字面量定義
let f2: { <T>(arg: T): T } = clone;

// 泛型接口
interface GenericFn{
    <T>(arg:T):T
}

interface GenericFn2<T>{
    (arg:T):T
}



let f3: GenericFn = clone;
let f3: GenericFn2<string> = clone;

複製代碼

泛型類

class Queue<T> {
  private data: T[] = [];
  push = (item: T) => this.data.push(item);
  pop = (): T | undefined => this.data.shift();
}

// 簡單的使用
const queue = new Queue<number>();
queue.push(0);
queue.push("1"); // Err: 接受的始類型應爲 number
複製代碼

泛型約束

  • extends 關鍵字
interface ILength {
    length: number;
}

function logger<T extends ILength>(n: T) {
    return n.length;
}
複製代碼
  • 約束類型參數
function getProp<T, K extends keyof T>(obj: T, prop: K) {
    return obj[prop];
}

let o = {
    name: "aaa",
    age: 111,
}

getProp(o,"name");
getProp(o,"age");
getProp(o,"b"); // Err 對象 "o" 上不存在屬性 "b"
複製代碼
  • 使用類類型
function createInstance<T>(Ctor: { new(): T }):T {
    return new Ctor;
}
複製代碼

枚舉

數字枚舉

  • 默認從 0 開始遞增
enum Direction {
  Up,    // 0
  Right, // 1
  Down,  // 2
  Left   // 3
}
複製代碼
  • 自定義遞增初始值
enum ServerCode {
  BadRequest = 400,   // 400
  NoAuth,             // 401
  BanAccess = 403,    // 403
  NotFound,           // 404
  ServerError = 500   // 500
}
複製代碼

字符串枚舉

enum Direction {
    Up = "UP",
    Down = "DOWN",
    Left = "LEFT",
    Right = "RIGHT",
}
複製代碼

異構枚舉

混入數字和字符串成員的枚舉c#

enum BooleanLikeHeterogeneousEnum {
    No = 0,
    Yes = "YES",
}
複製代碼

運行時枚舉

枚舉是運行時真正存在的對象數組

enum E {
    X,
    Y,
    Z
}

function f(obj: { X: number }) {
    return obj.X;
}

f(E)
複製代碼

編譯時枚舉

enum LogLevel {
    ERROR,
    WARN,
    INFO,
    DEBUG
}

type LogLevelStrings = keyof typeof LogLevel;

let status: LogLevelStrings = "ERROR";
status="666"; // Err: "666" 不能賦值給 "ERROR" | "WARN" | "INFO" | "DEBUG"

複製代碼

const 枚舉

常量枚舉不容許包含計算成員(只容許使用常量枚舉表達式)dom

const enum Enum {
    A = 1,
    B = 2 * A, // ok 常量枚舉表達式
    C = Math.random(), // Err: 計算成員
    D = (() => 1)() // Err: 計算成員 } 複製代碼

外部枚舉 ?

在正常的枚舉裏,沒有初始化方法的成員被當成常量成員。 對於很是量的外部枚舉而言,沒有初始化方法時被當作須要通過計算的編輯器

declare enum Enum {
    A = 1,
    B,
    C = 2
}
複製代碼

高級類型

交叉類型

同時擁有全部類型的所有成員函數

interface IA {
  name: string;
}

interface IB {
  age: number;
}

let d: IA & IB = {
  name: "",
  age: 1
};

複製代碼

聯合類型

只能是全部類型之一工具

function printSth(input: string | number) {
  return input;
}

printSth(1);
printSth("");

interface IC {
  name: string;
  c: number;
}
複製代碼

若是值是聯合類型,只能訪問全部類型的公有部分

interface IC {
  name: string;
  c: number;
}

interface ID {
  name: string;
  d: string;
}

function f3(): IC | ID {
  return {
    name: "",
    c: 1
  };
}

let c3 = f3();
c3.name;
c3.d; // Err: 聯合類型,只能訪問全部類型裏公有的部分

複製代碼

類型守衛與類型區分

使用聯合類型的值時,須要類型區分出明確的類型

  • 用戶定義類型守衛

    • 屢次使用類型斷言 as
    let pet = getSmallPet();
    
    if ((pet as Fish).swim) {
        (pet as Fish).swim();
    } else if ((pet as Bird).fly) {
        (pet as Bird).fly();
    }
    
    複製代碼
  • 使用類型斷定

    • 使用類型謂詞 is
    function isFish(pet:Fish|Bird):pet is Fish {
        return (pet as Fish).swim!==void 0;
    }
    
    複製代碼
    • 使用 in 操做符
    function move(pet:Fish|Bird) {
        if ("swim" in pet) {
            pet.swim();
        } else {
            pet.fly();
        }
    }
    複製代碼
  • typeof 類型守衛

function padLeft(m: string | number) {
    if (typeof m === "number") {
        return m + 1;
    } else {
        return m.trim();
    }
}
複製代碼
  • instanceof 類型守衛
function move2(pet: Fish | Bird) {
  if (pet instanceof Fish) {
    return pet.swim();
  } else {
    return pet.fly();
  }
}
複製代碼

能夠爲 null 的類型

默認狀況下,nullundefined 能夠賦值給任何類型,開啓 "--strictNullChecks" 標記解決此錯誤

  • 可選參數和可選屬性

使用了 "--strictNullChecks" 標記,可選參數會被自動加上 "|undefined"

interface IA{
    // name:string|undefined
    name?:string;
    age:number
}
複製代碼
  • 類型守衛和類型斷言

因爲使用聯合類型,可能會包含 null 類型,你須要使用類型守衛去除它

  • 顯示判斷
function f1(s:string|null){
    if (s===null) return;
    return s+"haha";
}
複製代碼
  • 短路運算符
function f1(s:string|null){
    let char=s||"";
    return s+"haha";
}
複製代碼
  • 添加語法後綴 !,去除 nullundefined

編輯器沒法去除嵌套函數的 nullundefined,因此須要添加語法後綴 !,來去除 nullundefined

function f11(s: string | null) {
  s = s || "";
  function f12() {
    return s!.toString();
  }
  return f12();
}
複製代碼

類型別名

// 基礎類型
type NewString = string;
// 函數
type TypeOfFn = (arg: string) => string;
// 泛型
type GenericObj<T> = {
  value: T;
};
type Tree<T> = {
  value: string;
  left: Tree<T>;
  right: Tree<T>;
} | null;

複製代碼
  • 類型別名 vs 接口

    • 類型別名不可重複聲明,而接口能夠
    type A={
        name: string;
    };
    // Err: 標識 "A" 重複
    type A={
        age: string;
    };
    
    
    interface IA {
      name: string;
    }
    // ok
    interface IA {
      age: number;
    }
    
    let a: IA = {
      name: "",
      age: 1
    };
    
    複製代碼

字符串字面量類型

type Easing = "ease-in" | "ease-out";
let status3: Easing = "ease-in";
status3="a"; // Err
複製代碼
  • 字符串字面量類型還能夠用於區分函數重載
function createElement(tagName: "img"): HTMLImageElement;
function createElement(tagName: "input"): HTMLInputElement;
// ... more overloads ...
function createElement(tagName: string): Element {
  // ... code goes here ...
  if (tagName === "img") {
    return new Image();
  }
  if (tagName === "input") {
    return document.createElement("input");
  }
  return document.createElement("div");
}

createElement("input");
createElement("img");
複製代碼

數字字面量類型

function sayCode(a: 1 | 2 | 3) {
  return a + 1;
}

sayCode(1);
sayCode(5); // Err
複製代碼

索引類型

使用索引類型,能夠檢查動態屬性名

keyof,爲 索引類型查詢操做符
T[K],爲 索引訪問操做符

let o3 = {
  a: 1,
  b: "2",
  c: true
};

function f8<T, K extends keyof T>(obj: T, prop: K): T[K] {
  return obj[prop];
}

f8(o3, "b");
f8(o3, "a");
f8(o3, "d"); // Err: 不存在屬性 "d"
複製代碼
  • 索引類型和字符串簽名

    • 若是你有一個帶有字符串簽名的類型 T ,那麼 keyof T 的結果會是 number|string,由於 js 中,object[1]object["1"] 均可以訪問屬性
    interface Dictionary {
        [key: string]: any;
    }
    
    let keys: keyof Dictionary; // keys:string | number
    複製代碼
    • 若是是數字簽名的話, 那麼 keyof T 的結果會是 number
    interface Dictionary {
        [key: number]: any;
    }
    let keys: keyof Dictionary; // keys:number
    複製代碼

映射類型

  • 映射類型
type Readonly<U> = {
  readonly [T in keyof U]: U[T];
};
type Partial<U> = {
  [T in keyof U]?: U[T];
};

type ReadonlyPerson = Readonly<IPerson>;
type PartialPerson = Partial<IPerson>;
複製代碼
  • 映射成員 (使用交叉類型)
// 這樣使用 使用交叉類型
type PartialWithNewMember<T> = {
  [P in keyof T]?: T[P];
} & { newMember: boolean };

// 不要這樣使用這會報錯!
type PartialWithNewMember<T> = {
   [P in keyof T]?: T[P];
   newMember: boolean;
}
複製代碼
  • 有條件類型
    T extends U ? X : Y 翻譯: 若是 T 能賦值給 U,那麼類型是 X,不然是 Y

使用工具類型

Partial 將 T 類型的屬性變爲可選屬性

type Partial<T>={
    [P in keyof T]?:T[P]
}

複製代碼

Required 將 T 類型的屬性變爲必選屬性

type Required<T>={
    // "-?" 表明去除可選 對應的還有 "+?" ,做用與 "-?" 相反,是把屬性變爲可選項
    [P in keyof T]-?:T[P]
}
複製代碼

Readonly 將 T 類型的屬性變爲只讀屬性

type Readonly<T>={
    readonly [P in keyof T]:T[P]
}
複製代碼

Record<K,T> 將 K 類型的屬性映射到 T 類型上

type Record<K extends keyof any,T>={
    [P in K]:T
}
複製代碼

Exclude<T,U> 將 T 類型上的 U 類型的屬性剔除 (求差集?)

type Exclude<T,U>=T extends U?never:T;
複製代碼

Extract<T,U> 提取 T 類型上的 U 類型屬性,做爲一個新的類型 (求交集?)

type Extract<T,U>=T extends U?T:never;
複製代碼

Pick<T,K> 從 T 類型中挑選 K 類型屬性

type Pick<T,K extends keyof T>={
    [P in K]:T[P]
}
複製代碼

Omit<T,K> 從 T 類型中剔除 K 類型的屬性

type Omit<T,K extends keyof any>=Pick<T,Exclude<keyof T,K>>
複製代碼

NonNullable 從 T 類型中剔除 null 和 undefined 類型

type NonNullable<T>=T extends null|undefined? never: T;
複製代碼

ReturnType 返回函數類型 T 的返回值類型

type ReturnType<T extends (...args:any)=>any>=T extends (...args:any)=> infer R ? R : any;
複製代碼

InstanceType 返回構造函數類型 T 的實例類型

type InstanceType<T extends new (...args:any):any> = T extends new (...args:any):infer R ? R : any;
複製代碼

ThisType 做爲上下文 this 類型的一個標記,須要開啓 "--noImplictThis"

interface ThisType<T>{
    
}
複製代碼

模塊和命名空間

模塊 (外部模塊)

ts 沿用 es6 的模塊概念

  • 導出

StringValidator.ts

export interface StringValidator {
    isSafe:boolena;
}
複製代碼
  • 導入

test.ts

import { StringValidator } from "./StringValidator";

class Validator implements StringValidator {
   isSafe:boolean = true; 
}

複製代碼
  • 默認導出

StringValidator.ts

export default {
    version: "1.1.1"
}
複製代碼
  • 導入其餘 js 庫 (要想描述非 TypeScript 編寫的類庫的類型,咱們須要聲明類庫所暴露出的 API )

    • 外部模塊

    utils.d.ts

    declare module "url" {
        export function parse() {
            // ...
        }
    }
    
    複製代碼

    test.ts

    /// <reference path="utils.d.ts" />
    import { parse } from "url";
    
    複製代碼
    • 外部模塊簡寫

    簡寫模塊裏全部導出的類型將是 any

    utils.d.ts

    declare module "url";
    
    複製代碼

    test.ts

    /// <reference path="utils.d.ts" />
    import { parse } from "url";
    // parse:any
    複製代碼
    • 模塊聲明通配符

    global.d.ts

    declare module "*.json";
    複製代碼

命名空間 (內部模塊)

  • 命名空間
namespace Validation {
    export interface StringValidator {
        isSafe:boolena;
    }

    export class Validator implements StringValidator {
       isSafe:boolean = true; 
    }
}

複製代碼
  • 分割到多個文件

Validation.ts

namespace Validation {
    export interface StringValidator {
        isSafe:boolena;
    }
}

複製代碼

KlassValidation.ts

/// <reference path="Validation.ts" />
namespace Validation {
    export class Validator implements StringValidator {
       isSafe:boolean = true; 
    }
}

複製代碼
  • 別名
namespace Validation {
    export interface StringValidator {
        isSafe:boolena;
    }
}


import SV = Validation.StringValidator;

複製代碼
  • 使用其餘的 js 庫

爲了描述不是用 TypeScript 編寫的類庫的類型,咱們須要聲明類庫導出的API。 因爲大部分程序庫只提供少數的頂級對象,命名空間是用來表示它們的一個好辦法。
咱們稱其爲聲明是由於它不是外部程序的具體實現。 咱們一般在 .d.ts 裏寫這些聲明

D3.d.ts

declare namespace D3 {
  export interface Selectors {
    select: {
      (selector: string): Selection;
      (element: EventTarget): Selection;
    };
  }

  export interface Event {
    x: number;
    y: number;
  }

  export interface Base extends Selectors {
    event: Event;
  }
}

declare var d3: D3.Base;
複製代碼
相關文章
相關標籤/搜索