TypeScript進階篇(二)

1、TS中的配置文件

基礎配置

執行命令javascript

tsc --init 
複製代碼

生成tsconfig.json文件,也就是TS的編譯配置文件。html

{
  "compilerOptions": {
    /* Basic Options */
    // "incremental": true,                   /* Enable incremental compilation */
    "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    // "lib": [],                             /* Specify library files to be included in the compilation. */
    // "allowJs": true,                       /* Allow javascript files to be compiled. */
    // "checkJs": true,                       /* Report errors in .js files. */
    // "jsx": "preserve",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
    // "declaration": true,                   /* Generates corresponding '.d.ts' file. */
    // "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */
    // "sourceMap": true,                     /* Generates corresponding '.map' file. */
    // "outFile": "./",                       /* Concatenate and emit output to single file. */
    // "outDir": "./",                        /* Redirect output structure to the directory. */
    // "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    // "composite": true,                     /* Enable project compilation */
    // "tsBuildInfoFile": "./",               /* Specify file to store incremental compilation information */
    // "removeComments": true,                /* Do not emit comments to output. */
    // "noEmit": true,                        /* Do not emit outputs. */
    // "importHelpers": true,                 /* Import emit helpers from 'tslib'. */
    // "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
    // "isolatedModules": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */

    /* Strict Type-Checking Options */
    "strict": true,                           /* Enable all strict type-checking options. */
    // "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */
    // "strictNullChecks": true,              /* Enable strict null checks. */
    // "strictFunctionTypes": true,           /* Enable strict checking of function types. */
    // "strictBindCallApply": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
    // "strictPropertyInitialization": true,  /* Enable strict checking of property initialization in classes. */
    // "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */
    // "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */

    /* Additional Checks */
    // "noUnusedLocals": true,                /* Report errors on unused locals. */
    // "noUnusedParameters": true,            /* Report errors on unused parameters. */
    // "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
    // "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */

    /* Module Resolution Options */
    // "moduleResolution": "node",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    // "baseUrl": "./",                       /* Base directory to resolve non-absolute module names. */
    // "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
    // "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
    // "typeRoots": [],                       /* List of folders to include type definitions from. */
    // "types": [],                           /* Type declaration files to be included in compilation. */
    // "allowSyntheticDefaultImports": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
    "esModuleInterop": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */
    // "allowUmdGlobalAccess": true,          /* Allow accessing UMD globals from modules. */

    /* Source Map Options */
    // "sourceRoot": "",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */
    // "mapRoot": "",                         /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */
    // "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */

    /* Experimental Options */
    // "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */
    // "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */

    /* Advanced Options */
    "forceConsistentCasingInFileNames": true  /* Disallow inconsistently-cased references to the same file. */
  }
}

複製代碼

編譯TS命令,直接指定編譯某個ts文件的時候並不會使用tsconfig.json中的內容,只有直接運行tsc時,纔會走tsconfig.json中的配置java

tsc dome.ts
複製代碼

只有直接運行tsc時,纔會走tsconfig.json中的配置。在未配置tsconfig.json的狀況下,會執行編譯全部的ts文件node

tsc // 在未配置tsconfig.json的狀況下,會執行編譯全部的ts文件
複製代碼

tsc命令會找tsconfig.json,react

"include": ["./demo.ts"], // 指定只編譯demo.ts文件
"exclude": ["./demo.ts"], // 排除編譯demo.ts文件,就不會編譯demo.ts
"files": ["./demo.ts"], // files與include功能相同
"removeComments": true // 編譯中移除註釋
複製代碼

也能夠寫成jquery

"include": ["src/**/*"], // ** 表明任何目錄,* 表明任何文件
複製代碼

參考文檔: tsconfig-jsonwebpack

compilerOptions字面意思:就是編譯過程當中的一些屬性或配置。git

"compilerOptions": {
  "removeComments": true,  // 編譯中移除註釋
  "noImplicitAny": false, // 不要求顯示的設置any,false就是能夠,這樣變量不定義類型也不會報錯。默認true
  "strictNullChecks": false, // 強制檢查null類型。默認true。不然編譯會報錯。
}
複製代碼

es6

const children: string = null // 不容許把null複製給其餘基礎類型 strictNullChecks設置爲false則不會報錯,編譯也不會報錯。
複製代碼

進階配置

  1. 指定文件的輸入地址,和文件的生成地址。
"rootDir": "./src",  // 輸入地址
"outDir": "./build",  // 生成地址
複製代碼
  1. 增量編譯。只編譯新增內容,以前編譯過的不會再編譯。打開後,首次編譯以後會生成tsconfig.tsbuildinfo文件,用來記錄上次編譯過程當中的信息,再次編譯的時候就會和上一次作比對。是一個增量式的配置項。
"incremental": true,  // 增量編譯
複製代碼
  1. 是否容許對js項也進行編譯。
"target": "es5", // 將代碼編譯成es5
"allowJs": true,  // 是否容許對js項也進行編譯
複製代碼

編譯前:github

export const name = 'dell'
複製代碼

編譯後

"use strict"
Object.defineProperty(exports, "__esModule", {value: true})
exports.name = 'dell'
複製代碼
  1. 對語法進行檢測。
"checkJs": true, // 對js文件進行語法檢測
複製代碼
  1. 生成sourceMap文件,***.js.map
"sourceMap": true,
複製代碼
  1. 變量未被使用時警告。
"noUnusedLocals": true, 
複製代碼
  1. 函數的參數未被使用時警告。
"noUnusedParameters": true, 
複製代碼

參考文檔:編譯選項

2、TS中的聯合類型Unin type和類型保護

用類型保護解決聯合類型遇到的問題

  1. 類型斷言 as
interface Bird {
  fly: boolean,
  sing: () => {},
}

interface Dog {
  fly: boolean,
  bark: () => {},
}

// 聯合類型 animal: Bird | Dog
function trainAnimal (animal: Bird | Dog) {
  // 類型保護 sing和bark是獨有的屬性
  // 類型斷言 (animal as Bird) 傳入的animal是Bird的時候
  if (animal.fly) { // Bird纔會飛true
    (animal as Bird).sing() // as 斷言
  } else {
    (animal as Dog).bark()
  }
}
複製代碼
  1. 類型保護,用 in語法
function trainAnimalSecond (animal: Bird | Dog) {
  if ('sing' in animal) {
    animal.sing()
  } else {
    animal.bark()
  }
}
複製代碼
  1. 類型保護,用 typeof語法

function add (first: string | number, second: string | number) {
  if (typeof first === 'string' || typeof second === 'string') {
    return `${first}${second}`
  }
  return first + second
}
複製代碼
  1. 類型保護,用 instanceof語法,只有爲class類的時候纔可使用,interface不能用instanceof
class NumberObj {
  public count: number
}

function addSecond (first: object | NumberObj, second: object | NumberObj) {
  if (first instanceof NumberObj && second instanceof NumberObj) {
    return first.count + second.count
  }
  return 0
}
複製代碼

2、TS中的枚舉類型Enum

  1. 用js來模擬枚舉
const Status = {
  OFFLINE: 0,
  ONLINE: 1,
  DELETED: 2
};

function getResult(status) {
  if (status === Status.OFFLINE) {
    return 'offline';
  } else if (status === Status.ONLINE) {
    return 'online';
  } else if (status === Status.DELETED) {
    return 'deleted';
  } else {
    return 'error';
  }
}

const result = getResult(Status.OFFLINE);
console.log(result); // online
複製代碼
  1. ts來寫枚舉
enum Status {
  OFFLINE,
  ONLINE,
  DELETED
}
// 枚舉默認值爲 0 1 2
console.log(Status.OFFLINE); // 0
console.log(Status.ONLINE);  // 1
console.log(Status.DELETED); // 2

function getResult(status) {
  if (status === Status.OFFLINE) {
    return 'offline';
  } else if (status === Status.ONLINE) {
    return 'online';
  } else if (status === Status.DELETED) {
    return 'deleted';
  } else {
    return 'error';
  }
}

const result = getResult(Status.OFFLINE);
const result1 = getResult(0);
console.log(result); // offline
console.log(result1); // offline
複製代碼

輸出結果,依舊不變。

改變offline的枚舉值

enum Status {
  OFFLINE = 1,
  ONLINE,
  DELETED
}
console.log(Status.OFFLINE); // 1
console.log(Status.ONLINE);  // 2
console.log(Status.DELETED); // 3
複製代碼
enum Status {
  OFFLINE, // 默認從0 開始
  ONLINE = 4,
  DELETED //  從4開始加1
}
console.log(Status.OFFLINE); // 0
console.log(Status.ONLINE);  // 4
console.log(Status.DELETED); // 5
複製代碼

枚舉類型還能夠反查結果(反向映射),以下:

enum Status {
  OFFLINE,
  ONLINE,
  DELETED
}
console.log(Status[0]); // OFFLINE
複製代碼

enum是一個更靈活的數據結構。

使用場景

  1. 和咱們的例子相似,當咱們的某一個狀態的值是固定的幾個值,此時就能夠用枚舉類型來表示這幾個值,更加清晰易懂。
  2. 枚舉默認值從0開始,也能夠手動修改枚舉值。
  3. 還能夠經過對應下標0、一、2...反向查枚舉的值。若是枚舉值被修改,則須要寫對應修改的值便可,如console.log(Status[4])ONLINE

小插曲:在安裝typescirpt的時候居然報了以下錯誤,緣由是項目的name也是typescript,修改以後便可正常安裝。

npm ERR! code ENOSELF
npm ERR! Refusing to install package with name "typescript" under a package
npm ERR! also called "typescript". Did you name your project the same
npm ERR! as the dependency you're installing? npm ERR! npm ERR! For more information, see: npm ERR! <https://docs.npmjs.com/cli/install#limitations-of-npms-install-algorithm> 複製代碼
{
  "name": "typescript",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "devDependencies": {
    "ts-node": "^8.6.2"
  },
  "scripts": {
    "dev": "ts-node ./src/enum.ts"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
複製代碼

4、TS中的函數泛型

泛型generic:泛指的類型,寫法<>

針對不肯定的類型就能夠用泛型語法,在使用的時候再指定具體的對應值

示例1

// <ABC> 定義名字爲ABC的泛型,兩個傳入的參數都要求爲同一類型
function join<ABC>(first: ABC, second: ABC) {
  return `${first}${second}`
}
// 在使用時給ABC指定類型,定義類型都爲string,傳入的值就必須爲string類型
join<string>('1','2')
複製代碼

以下寫法,則沒法作到兩個參數傳入值必須爲同一類型的要求。

function join(first: string | number, second: string | number) {
  return `${first}${second}`
}
join('1',2)
複製代碼

示例2

// function map<ABC> (params: Array<ABC>) {
function map<ABC> (params: ABC[]) {
  return params
}
map<string>(['1', '2', '3'])
複製代碼

常見用T來代替ABC,也就是Type的簡寫

// function map<T> (params: Array<T>) {
function map<T> (params: T[]) {
  return params
}
map<string>(['1', '2', '3'])
複製代碼

咱們還能夠定義兩個泛型或多個泛型

function join<T, P>(first: T, second: P) {
  return `${first}${second}`;
}
join<string, number>('1', 2); // 顯式聲明類型
join<('1', 2) // 不寫類型則會默認推斷
複製代碼

不寫類型則會默認推斷(類型推斷),以下圖:

聲明返回類型也用T這個泛型,以下:

function anotherJoin<T>(first: T, second: T): T {
  return first;
}
anotherJoin<string>('1', '2');
複製代碼

泛型和普通的類型使用基本是同樣的。使用的時候須要在函數前用<>作一個聲明

5、TS中類的泛型以及泛型類型

Demo1

class DataManager<T> {
  constructor(private data: T[]) {}
  getItem(index: number): T {
    return this.data[index];
  }
}
const data = new DataManager<number>([2]);
data.getItem(0);
複製代碼

Demo2: 讓泛型繼承某一屬性

interface Item {
  name: string;
}
class DataManager<T extends Item> {
  constructor(private data: T[]) {}
  getItem(index: number): string {
    return this.data[index].name;
  }
}
const data = new DataManager([{ name: '2' }]);
data.getItem(0);
複製代碼

Demo3: 泛型是numberstring<T extends number | string>

class DataManager<T extends number | string> {
  constructor(private data: T[]) {}
  getItem(index: number): T {
    return this.data[index];
  }
}
const data = new DataManager<number>([2])
複製代碼

Demo4: 如何使用泛型做爲一個具體的類型註解

// 泛型還能夠當作type的聲明
function hello<T>(params: T) {
  return params
}
const func:<T>(param: T) => T = hello
複製代碼

6、TS中命名空間-namespace

過多的全局變量必定會讓頁面變的不可維護。

編譯前:

// 把全部東西都放到Home這樣的命名空間中,防止Header、Content、Footer都暴露在全局環境中
namespace Home {
  class Header {
    constructor() {
      const element = document.createElement('div');
      element.innerHTML = 'This is Header';
      document.body.appendChild(element);
    }
  }

  class Content {
    constructor() {
      const element = document.createElement('div');
      element.innerHTML = 'This is Content';
      document.body.appendChild(element);
    }
  }

  class Footer {
    constructor() {
      const element = document.createElement('div');
      element.innerHTML = 'This is Footer';
      document.body.appendChild(element);
    }
  }
  export class Page { // 導出Page
    constructor() {
      new Header();
      new Content();
      new Footer();
    }
  }
}

new Home.Page();
複製代碼

編譯後:

// 把全部東西都放到Home這樣的命名空間中
var Home;
(function (Home) {
    var Header = /** @class */ (function () {
        function Header() {
            var element = document.createElement('div');
            element.innerHTML = 'This is Header';
            document.body.appendChild(element);
        }
        return Header;
    }());
    var Content = /** @class */ (function () {
        function Content() {
            var element = document.createElement('div');
            element.innerHTML = 'This is Content';
            document.body.appendChild(element);
        }
        return Content;
    }());
    var Footer = /** @class */ (function () {
        function Footer() {
            var element = document.createElement('div');
            element.innerHTML = 'This is Footer';
            document.body.appendChild(element);
        }
        return Footer;
    }());
    var Page = /** @class */ (function () {
        function Page() {
            new Header();
            new Content();
            new Footer();
        }
        return Page;
    }());
    Home.Page = Page; // 將Page方法暴露出來
})(Home || (Home = {}));
new Home.Page();
複製代碼

這種寫法可讓咱們盡少的聲明全局變量,把一組相關的內容封裝到一塊兒,對外提供統一的接口。

// tsconfig.json 
"outFile": "./build/page.js", // 將多個ts文件統一輸出到一個js文件中./build/page.js文件中
"module": "amd"
複製代碼

將文件拆成page.tscomponents.ts

// components.ts
namespace Components {
  // 子命名空間
  export namespace SubComponents {
    export class Test{}
  }
  // 導出接口
  export interface User {
    name: string;
  }
  
  export class Header {
    constructor() {
      const element = document.createElement('div');
      element.innerHTML = 'This is Header';
      document.body.appendChild(element);
    }
  }

  export class Content {
    constructor() {
      const element = document.createElement('div');
      element.innerHTML = 'This is Content';
      document.body.appendChild(element);
    }
  }

  export class Footer {
    constructor() {
      const element = document.createElement('div');
      element.innerHTML = 'This is Footer';
      document.body.appendChild(element);
    }
  }
}

複製代碼
// page.ts
依賴的聲明,namespace之間相互引用的聲明,用///表示
/// <reference path='./components.ts' />
namespace Home {
  export class Page {
    user: Components.User = {
        name: 'fruit'
    }
    constructor() {
      new Components.Header();
      new Components.Content();
      new Components.Footer();
    }
  }
}

new Home.Page();
複製代碼

7、TS中import對應的模塊化

amd語法瀏覽器是不支持的

// components.ts
export class Header {
  constructor() {
    const element = document.createElement('div');
    element.innerHTML = 'This is Header';
    document.body.appendChild(element);
  }
}

export class Content {
  constructor() {
    const element = document.createElement('div');
    element.innerHTML = 'This is Content';
    document.body.appendChild(element);
  }
}

export class Footer {
  constructor() {
    const element = document.createElement('div');
    element.innerHTML = 'This is Footer';
    document.body.appendChild(element);
  }
}

複製代碼

import 模塊引入(es6用法)

// page.ts
import { Header, Content, Footer } from './components';
class Page {
  constructor() {
    new Header();
    new Content();
    new Footer();
  }
}

new Page();
複製代碼

參考文檔:

www.zhihu.com/question/20…

zhuanlan.zhihu.com/p/41231046

zhuanlan.zhihu.com/p/25107397?…

8、使用Parcel打包TS代碼

Parcel:是和webpack相似的打包工具,不須要作過多配置,使用起來相對簡單。

npm init -y
複製代碼
tsc --init
複製代碼

parcel : github.com/parcel-bund…

npm install parcel@next -D 
複製代碼
// package.json
"scripts": {
    "test": "parcel ./src/index.html"
 },
複製代碼

運行

npm run test
複製代碼

在瀏覽器訪問 http://localhost:1234/便可查看相關運行結果

9、描述文件中的全局類型

類型定義文件***.d.ts 如何在.d.ts文件裏對全局函數和全局變量進行定義

// jquery.d.ts
// 定義全局變量
declare var $: (param: () => void) => void;
// 定義全局函數
declare function $(param: () => void): void;
// 容許一個函數屢次定義。函數的重載,根據傳入參數的不一樣變化
declare function $(params: string): {
  html: (html: string) => {}
}
複製代碼

寫法優化

// 定義全局函數
interface JqueryInstance {
  html: (html: string) => JqueryInstance;
}
// 函數重載
declare function $(readyFunc: () => void): void;
declare function $(selector: string): JqueryInstance;
複製代碼

使用$就不會報錯

$(function() {
  $('div').html('<div>123</div>');
});
複製代碼

爲何咱們要安裝或本身寫類型定義文件,幫助咱們的ts文件,理解js文件或js庫中的內容。好比$

**.d.ts爲類型描述文件或類型定義文件

使用interface實現函數重載

// $只是不一樣的函數,只是對函數重載的時候,能夠用下面的語法
interface JQuery {
  (readyFunc: () => void): void
  (selector: string): JqueryInstance
}
declare var $: JQuery;
複製代碼
// 如何對對象進行類型定義,以及如何對類進行類型定義,以及命名空間的嵌套
// 既讓$是函數,又讓$是對象,就能夠用下面的語法
declare namespace $ { // 在全局有對象,能夠用namespace來構建這個對象
  namespace fn {
    class init {}
  }
}

// 引入外部的庫,TS沒法識別,咱們就能夠用全局聲明內容的語法,讓ts可以理解,$裏都有哪些內容
$(function() {
  $('div').html('<div>123</div>');
  new $.fn.init();
});
複製代碼

引入外部的庫,TS沒法識別,咱們就能夠用全局聲明內容的語法,讓TS可以理解,$裏都有哪些內容

參考文檔:TypeScript error: Property 'X' does not exist on type 'Window'

當咱們使用window時,好比 let a = window.a,將a掛載到window下,此時TS會報以下錯誤:

TypeScript error: Property 'a' does not exist on type 'Window'. TS2339
複製代碼

以下,使用type定義依舊無效

type Window = {
  a: any
}

let a = window.a;
複製代碼

解決方法

declare const window: any;
let a = window.a;
複製代碼

10、模塊代碼的類型描述文件

es六、commonjs、UMD 等在TS中如何定義

npm install jquery --save
複製代碼
import $ from 'jquery'
複製代碼

報以下錯誤

此時咱們須要模塊化的描述文件。

ES6 模塊化的類型註解文件,就是這麼寫出來的。Commonjs UMD等寫法和ES6寫法稍有不一樣,可查閱相關資料。

// jquery.d.ts
// ES6 模塊化
declare module 'jquery' {
  interface JqueryInstance {
    html: (html: string) => JqueryInstance;
  }
  // 混合類型
  function $(readyFunc: () => void): void;
  function $(selector: string): JqueryInstance;
  namespace $ {
    namespace fn {
      function init(): void;
    }
  }
  // 導出
  export = $;
}
複製代碼

這樣使用$就不會報錯了

import $ from 'jquery';

$(function() {
  $('div').html('<div>123</div>');
  new $.fn.init();
});
複製代碼

11、泛型中keyof語法的使用

interface Persone {
  name: string;
  age: number;
  gender: string;
}
class Student {
  constructor(private info: Persone) {}
  getInfo(key: string) {
    return this.info[key]; // 咱們沒法肯定key的值
  }
}

const student1 = new Student({
  name: 'fruit',
  age: 18,
  gender: 'male'
});

const studentName = student1.getInfo('name');
複製代碼

studentName的類型返回的爲any

以上的寫法, key值是不安全的,當傳入的值爲 nameagegender以外的值時,就會返回 undefined,所以咱們須要類型保護來解決這個問題。

修改寫法:但不夠好,傳入這三個值以外的值是,仍是會返回undefined

getInfo(key: string) {
  if (key === 'name' || key === 'age' || key === 'gender') {
    return this.info[key];
  }
}
複製代碼

泛型結合keyof來寫

type T = 'name'
key: 'name'
Person1['name']

type T = 'age'
key: 'age'
Person1['age']

type T = 'gender'
key: 'gender'
Person1['gender']
複製代碼
interface Person1 {
  name: string;
  age: number;
  gender: string;
}

class Student {
  constructor(private info: Person1) {}
  getInfo<T extends keyof Person1>(key: T): Person1[T] {
    return this.info[key];
  }
}

const student1 = new Student({
  name: 'fruit',
  age: 18,
  gender: 'male'
});

const studentName = student1.getInfo('hello'); // 不能傳hello,只能是name、age、gender
複製代碼

當咱們定義一個類型的時候,可讓類型不是string、number等基礎類型,也能夠是interface、{}對象等複雜類型,咱們的類型甚至是固定的字符串,如: type T = 'name',類型的值就是一個字符串。

type A = 'NAME'
const a: A = 'bc'
複製代碼

報以下錯誤:

// 這樣就不報錯,變量的值必須和type'NAME'字符串同樣的東西
type A = 'NAME'
const a: A = 'NAME'
複製代碼

所以,類型也能夠是一個字符串。正是由於這個原理,咱們才能夠用keyof語法,結合泛型,實現上述效果(只能傳遞對應的key)。若是有一個類,裏面有一個對象,咱們想根據index、或key值獲取對象裏的某項內容時,又想要推斷出正確的返回類型,就能夠用相似這樣的語法來解決<T extends keyof Person1>(key: T): Person1[T]

相關文章
相關標籤/搜索