【TypeScript 演化史 -- 11】泛型參數默認類型 和 新的 --strict 編譯選項

做者:Marius Schulz
譯者:前端小智
來源: https://mariusschulz.com/
點贊再看,養成習慣

本文 GitHub https://github.com/qq44924588... 上已經收錄,更多往期高贊文章的分類,也整理了不少個人文檔,和教程資料。歡迎Star和完善,你們面試能夠參照考點複習,但願咱們一塊兒有點東西。javascript

TypeScript 2.3 增長了對聲明泛型參數默認類型的支持,容許爲泛型類型中的類型參數指定默認類型。前端

接下來看看如何經過泛型參數默認將如下React組件從 JS (和JSX)遷移到 TypeScript (和TSX):java

class Greeting extends React.Component {
  render() {
    return <span>Hello, {this.props.name}!</span>;
  }
}

爲組件類建立類型定義

我們先從爲 Component 類建立類型定義開始。每一個基於類的 React 組件都有兩個屬性: propsstate,類型定義結構大體以下:node

declare namespace React {
  class Component {
    props: any;
    state: any;
  }
}

注意,這個是大大簡化的示例,由於我們是爲了演示泛型類型參數及其默認值的內容。react

如今就能夠經過繼承來調用上面定義的 Component:git

class Greeting extends React.Component {
  render() {
    return <span>Hello, {this.props.name}!</span>
  }
}

我們能夠以下方式建立組件的實例:github

<Greeting name="world" />

渲染上面組件會生成如下 HTML:面試

<span>Hello, World!</span>

nice,繼續。typescript

使用泛型類型定義 Props 和 State

雖然上面的示例編譯和運行得很好,可是我們的 Component 類型定義不是很精確。由於我們將 propsstate 類型設置爲 any,因此 TypeScript 編譯器也幫不上什麼忙。express

我們得更具體一點,經過兩種泛型類型: PropsState,這樣就能夠準確地描述 propsstate 屬性的結構。

declare namespace React {
  class Component <Props, State> {
    props: Props;
    state: State;
  }
}

接着建立一個GreetingProps類型,該類型定義一個字符串類型 name 的屬性,並將其做爲Props類型參數的類型參數傳遞:

type GreetingProps = { name: string };

class Greeting extends React.Component<GreetingProps, any> {
  render() {
    return <span>Hello, {this.props.name}!</span>;
  }
}

1) GreetingProps 是類型參數Props的類型參數

2) 相似地,any是類型參數 State 的類型參數

有了這些類型,我們的組件獲得更好的類型檢查和自動提示:

clipboard.png

可是,如今使用 React.Component 類時就必需供兩種類型。我們開着的初始代碼示例就不在正確地進行類型檢查:

// Error: 泛型類型 Component<Props, State>
// 須要 2 個類型參數。
class Greeting extends React.Component {
  render() {
    return <span>Hello, {this.props.name}!</span>;
  }
}

若是我們不想指定像GreetingProps這樣的類型,能夠經過爲PropsState類型參數提供any類型來修正代碼:

class Greeting extends React.Component<any, any> {
  render() {
    return <span>Hello, {this.props.name}!</span>;
  }
}

這種方法可讓編譯器經過,但我們還有更優雅的作法:泛型參數默認類型

泛型參數默認類型

從 TypeScript 2.3 開始,我們能夠爲每一個泛型類型參數添加一個默認類型。在下面的例子中,若是沒有顯式地給出類型參數,那麼 PropsState 都都是 any 類型:

declare namespace React {
  class Component<Props = any, State = any> {
    props: Props;
    state: State;
  }
}

如今,我們就能夠不用指定泛型類型就能夠經過編譯器的檢查:

class Greeting extends React.Component {
  render() {
    return <span>Hello, {this.props.name}!</span>;
  }
}

固然,我們仍然能夠顯式地爲Props類型參數提供類型並覆蓋默認的any類型,以下所示:

type GreetingProps = { name: string };

class Greeting extends React.Component<GreetingProps, any> {
  render() {
    return <span>Hello, {this.props.name}!</span>;
  }
}

這兩個類型參數如今都有一個默認類型,因此它們是可選的,我們能夠僅爲Props指定顯式的類型參數:

type GreetingProps = { name: string };

class Greeting extends React.Component<GreetingProps> {
  render() {
    return <span>Hello, {this.props.name}!</span>;
  }
}

注意,我們只提供了一個類型參數。可是,被省略可選類型參數前一個必需要指定類型,不然不能省略。

其它事例

在上一篇中關於 TypeScript 2.2 中混合類的文章中,我們最初聲明瞭如下兩個類型別名:

type Constructor<T> = new (...args: any[]) => T;
type Constructable = Constructor<{}>;

Constructable類型純粹是語法糖。它能夠代替 Constructor<{}> 類型,這樣就沒必要每次都要寫泛型類型參數。使用泛型參數默認值,就能夠徹底去掉附加的可構造類型,並將{}設置爲默認類型

type Constructor<T = {}> = new (...args: any[]) => T;

語法稍微複雜一些,可是生成的代碼更簡潔,Good。

新的 --strict 主要編譯選項

TypeScript 2.3 引入了一個新的 --strict 編譯器選項,它支持許多與更嚴格的類型檢查相關的其餘編譯器選項。

TypeScript 加入的新檢查項爲了不不兼容現有項目一般都是默認關閉的。雖然避免不兼容是好事,但這個策略的一個弊端則是使配置最高類型安全愈來愈複雜,這麼作每次 TypeScript 版本發佈時都須要顯示地加入新選項。有了--strict編譯選項,就能夠選擇最高級別的類型安全(瞭解隨着更新版本的編譯器增長了加強的類型檢查特性可能會報新的錯誤)。

新的--strict編譯器選項包含了一些建議配置的類型檢查選項。具體來講,指定--strict至關因而指定了如下全部選項(將來還可能包括更多選項):

  • --strictNullChecks
  • --noImplicitAny
  • --noImplicitThis
  • --alwaysStrict

將來的 TypeScript 版本可能會在這個集合中添加額外的類型檢查選項。這意味着我們不須要監控每一個 TypeScript 版原本得到應該在項目中啓用的新嚴格性選項。若是向上述選項集添加了新選項,則在升級項目的 TypeScript 版本後,它們將自動激活。

--strict 編譯選項會爲以上列出的編譯器選項設置默認值。這意味着還能夠單獨控制這些選項。好比:

--strict --noImplicitThis false

或者在tsconfig.json 文件指定:

{
  "strict": true,
  "alwaysStrict": false
}

這將是開啓除--noImplicitThis編譯選項之外的全部嚴格檢查選項。使用這個方式能夠表述除某些明確列出的項之外的全部嚴格檢查項。換句話說,如今能夠在默認最高級別的類型安全下排除部分檢查。

改進的 --init 輸出

除了默認的--strict設置外,tsc --init還改進了輸出。tsc --init默認生成的tsconfig.json文件如今包含了一些帶描述的被註釋掉的經常使用編譯器選項. 你能夠去掉相關選項的註釋來得到指望的結果。咱們但願新的輸出能簡化新項目的配置而且隨着項目成長保持配置文件的可讀性。

經過 tsc --init 編譯器能夠爲構建一個配置文件:

$ tsc --init
message TS6071: Successfully created a tsconfig.json file.

運行此命令後,會當前工做目錄中生成一個tsconfig.json文件,生成的配置以下所示:

{
  "compilerOptions": {
    /* Basic Options */
    "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
    "module": "commonjs",                     /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd' or 'es2015'. */
    // "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. */
    // "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. */
    // "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. */
    // "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. */

    /* 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. */
  }
}

注意 --strict 是默認啓用的。這意味着在啓動一個新的TypeScript項目時,自動進入默認模式。

--checkJS 選項下 .js 文件中的錯誤

即使使用了--allowJs,TypeScript 編譯器默認不會報 .js 文件中的任何錯誤。TypeScript 2.3 中使用--checkJs選項,.js文件中的類型檢查錯誤也能夠被報出.

你能夠經過爲它們添加// @ts-nocheck註釋來跳過對某些文件的檢查,反過來你也能夠選擇經過添加// @ts-check註釋只檢查一些.js文件而不須要設置--checkJs編譯選項。你也能夠經過添加// @ts-ignore到特定行的一行前來忽略這一行的錯誤.

.js文件仍然會被檢查確保只有標準的 ECMAScript 特性,類型標註僅在.ts文件中被容許,在.js中會被標記爲錯誤。JSDoc註釋能夠用來爲你的 JS 代碼添加某些類型信息,


代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug

原文:
https://mariusschulz.com/blog...
https://mariusschulz.com/blog...


交流

乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。

https://github.com/qq44924588...

我是小智,公衆號「大遷世界」做者,對前端技術保持學習愛好者。我會常常分享本身所學所看的乾貨,在進階的路上,共勉!

關注公衆號,後臺回覆福利,便可看到福利,你懂的。

clipboard.png

相關文章
相關標籤/搜索