一憎妙、版本特性
4.1
- 模板字符串中可以使用字符串類型
type World = "world";
type Greeting = `hello ${World}`;
// example1
type VerticalAlignment = "top" | "middle" | "bottom";
type HorizontalAlignment = "left" | "center" | "right";
declare function setAlignment(value: `${VerticalAlignment}-${HorizontalAlignment}`): void;
// example2
let person = makeWatchedObject({
firstName: "Homer",
age: 42, // give-or-take
location: "Springfield",
});
person.on("firstNameChanged", () => {
console.log(`firstName was changed!`);
});
type PropEventSource<T> = {
on(eventName: `${string & keyof T}Changed`, callback: () => void): void;
};
declare function makeWatchedObject<T>(obj: T): T & PropEventSource<T>;
- Key Remapping in Mapped Types
// 新語法 k in keyof T as NewKeyType
type MappedTypeWithNewKeys<T> = {
[K in keyof T as NewKeyType]: T[K]
}
// example1
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
};
interface Person {
name: string;
age: number;
location: string;
}
type LazyPerson = Getters<Person>;
// equal to
type LazyPerson = {
getName: () => string;
getAge: () => number;
getLocation: () => string;
}
// example2
type RemoveKindField<T> = {
[K in keyof T as Exclude<K, "kind">]: T[K]
};
interface Circle {
kind: "circle";
radius: number;
}
type KindlessCircle = RemoveKindField<Circle>;
- Recursive Conditional Types
type ElementType<T> = T extends ReadonlyArray<infer U> ? ElementType<U> : T;
function deepFlatten<T extends readonly unknown[]>(x: T): ElementType<T>[] {
throw "not implemented";
}
// All of these return the type 'number[]':
deepFlatten([1, 2, 3]);
deepFlatten([[1], [2, 3]]);
deepFlatten([[1], [[2]], [[[3]]]]);
- --noUncheckedIndexedAccess 默認關閉煮岁,不隨 --strictNullChecks 打開
interface Options {
path: string;
permissions: number;
[propName: string]: string | number;
}
const opt:Options
opt.xxx string|number|undefined
// 影響 for 循環(huán)能岩,不影響for-of,forEach
function screamLines(strs: string[]) {
// This will have issues
for (let i = 0; i < strs.length; i++) {
console.log(strs[i].toUpperCase());
Object is possibly 'undefined'.
}
}
- paths without baseUrl
- checkJs Implies allowJs
- Breaking Changes
// resolve’s Parameters Are No Longer Optional in Promises
new Promise((resolve) => {
resolve() // error
});
// fix
new Promise<void>((resolve) => {
resolve() // work
});
4.0
- 可變元祖類型
1、元祖類型可以使用擴展運算符了伺通,之前只能對數(shù)組類型用
2答渔、擴展元算符可以用在任意位置了,之前只能用在最后一個參數(shù)
type IT = [string,boolean]
function tail<T extends any[]>(arr: readonly [any, ...IT]) {
const [_ignored, ...rest] = arr;
return rest;
}
type Strings = [string, string];
type Numbers = [number, number];
type StrStrNumNumBool = [...Strings, ...Numbers, boolean];
// 不限參數(shù)長度
type Strings = [string, string];
type Numbers = number[];
type Unbounded = [...Strings, ...Numbers, boolean];
// ==> type Unbounded = [string, string, ...(number | boolean)[]]
- 標記的元祖類型
下面這種寫法
function foo(...args: [string, number]): void {
// ...
}
是這種提示:
而這種寫法的提示語意上更為明確
type IRange = [start: string, end: number];
function foo(...args: IRange): void {
// ...
}
foo()
- 構造函數(shù)的類屬性推斷
現(xiàn)在類屬性在構造函數(shù)中被初始化后會被推斷出類型治专,之前一直是 any - 短路分配運算符
新加了&&=,||=遭顶,和??=
if(!a){
a=b
}
===>
a||=b
- catch 的 error 參數(shù)類型由 any 轉為了 unknown
- 啟動時的部分編譯功能
4.0 支持在 VSCode 啟動時優(yōu)先編譯當前工作區(qū)打開的文件张峰,而不是等所有的項目都編譯一遍才開始提供類型等信息
3.9
- 修復 Promise.all 類型推斷錯誤問題
interface Lion {
roar(): void;
}
interface Seal {
singKissFromARose(): void;
}
async function visitZoo(
lionExhibit: Promise<Lion>,
sealExhibit: Promise<Seal | undefined>
) {
let [lion, seal] = await Promise.all([lionExhibit, sealExhibit]);
lion.roar(); // uh oh
// lion is possibly 'undefined'.
}
- 提供 ts-expect-error 注釋
與 ts-ignore 的區(qū)別在于,如果下一行沒有 ts 錯誤棒旗,ts-ignore 啥也不干喘批,但 ts-expect-error 本身會報錯 - 條件表達式中的未調(diào)用函數(shù)檢查
條件表達式中只寫函數(shù)名而沒加括號會報錯 - 解析可選鏈接和非null斷言中的差異
// before, this transform is a bug
foo?.bar!.baz; => (foo?.bar).baz;
// now
foo?.bar!.baz; => foo?.bar.baz;
- 交叉類型和可選屬性的更嚴格檢查
interface A {
a: number; // notice this is 'number'
}
interface B {
b: string;
}
interface C {
a?: boolean; // notice this is 'boolean'
b: string;
}
declare let x: A & B;
declare let y: C;
// before is ok , now is error
y = x;
- getter/setter 不再可枚舉
- 類型參數(shù)any不再擴展any
function foo<T extends any>(arg: T) {
arg.spfjgerijghoied; // before is ok , now is error!
}
3.x
- 支持 reference 配置
- ReadonlyArray 類型
數(shù)組內(nèi)元素不能增加、修改和刪除
function foo(arr: ReadonlyArray<string>) {
arr.slice(); // okay
arr.push("hello!"); // error!
}
也可寫成 readonly string[]铣揉,同時也支持 tuples 類型:readonly [string,number]
- const 斷言
as const 可以將一些類型變?yōu)橹蛔x:
// Type '"hello"'
let x = "hello" as const;
// Type 'readonly [10, 20]'
let y = [10, 20] as const;
// Type '{ readonly text: "hello" }'
let z = { text: "hello" } as const;
- 3.5 引入 Omit 類型
type Person = {
name: string;
age: number;
location: string;
};
type QuantumPerson = Omit<Person, "location">;
// equivalent to
type QuantumPerson = {
name: string;
age: number;
};
- 3.7 引入可選鏈
// Before
if (foo && foo.bar && foo.bar.baz) {
// ...
}
// After
if (foo?.bar?.baz) {
// ...
}
- 3.7 引入空位合并
let x = foo ?? bar();
// equal to
let x = foo !== null && foo !== undefined ? foo : bar();
注意和 || 運算不同饶深,|| 包含了更多 falsy 類的值,如 false,'',0 等
- 3.8 引入類型導入和導出
import type { SomeThing } from "./some-module.js";
export type { SomeThing };
- 3.8 真-私有字段
之前都是 private逛拱,但這只在編譯階段有約束敌厘,編譯成JS后仍可訪問。3.8 的私有字段語法為: #valuableName橘券,編譯成JS后無法訪問额湘,實現(xiàn)上用到了 WeakMap
class C {
#foo = 10;
cHelper() {
return this.#foo;
}
}
二、小技巧
- 枚舉 key-value 互取
enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green; // 2
let colorName: string = Color[Color.Green]; // 'Green'
- Record
如果定義一個對象類型旁舰,直接 Record<string,unknown>锋华,而不是用 interface,interface寫起來麻煩 - Partial
將一個已定義好的接口必選屬性改為可選屬性箭窜,類似的還有 Readonly毯焕,Required,Omit<T,K>磺樱,Pick<T,K> - 回調(diào)函數(shù)類型
type Cb = ()=>void
- 當為一個默認值定義類型時纳猫,可以用 type,而不是 interface竹捉。能少寫代碼
// bad
interface IState{
loading: boolean;
data:any[];
}
const defalutState:IState={
loading:true,
data:[]
}
// good
const defalutState = {
loading:true,
data:[]
}
type IState = typeof defalutState
- 導入其它非 js 類的文件芜辕,需要自己聲明模塊
declare module "*!text" {
const content: string;
export default content;
}
// Some do it the other way around.
declare module "json!*" {
const value: any;
export default value;
}
import fileContent from "./xyz.txt!text";
import data from "json!http://example.com/data.json";
- 類型沖突時,可以用 reference 指定使用哪個類型文件块差,比如侵续,@types/node 和 @types/webpack-env 這兩個文件都聲明了 NodeJS,但有時 ts 找不到 webpack-env 里對 NodeJS 的 Module 接口擴展憨闰,導致 module.hot 會報錯
- 巧用查找類型
interface Person {
addr: {
city: string,
street: string,
num: number,
}
}
當需要使用 addr 的類型時, 可以直接 Person["addr"]
- 巧用ClassOf
abstract class Animal extends React.PureComponent {
/* Common methods here. */
}
class Cat extends Animal {}
class Dog extends Animal {}
// `AnimalComponent` must be a class of Animal.
const renderAnimal = (AnimalComponent: Animal) => {
return <AnimalComponent/>; // WRONG!
}
上面的代碼是錯的状蜗,因為 Animal 是實例類型,不是類本身鹉动。應該
interface ClassOf<T> {
new (...args: any[]): T;
}
const renderAnimal = (AnimalComponent: ClassOf<Animal>) => {
return <AnimalComponent/>; // Good!
}
renderAnimal(Cat); // Good!
renderAnimal(Dog); // Good!
- 為第三方庫寫類型文件
首先我們需要明確包使用的導出規(guī)范轧坎,global/umd/commonjs/module 等
對于 global 導出的包我們使用:
declare namesapce MyLib {
class A {}
// 我們可以直接在代碼中使用
// const a = new MyLib.A()
}
對于 umd/commonjs 導出的包我們使用:
declare module 'my-lib' {
namespace MyLib {
class A {}
class B {}
// 使用時
// 我們可以使用
// import * as MyLib from 'my-lib'
// const a = new MyLib.A();
// 如果開啟了 ES Module 融合模式 (esModuleInterop=true)
// 我們可以使用
// import { A } from 'my-lib'
// const a = new A()
}
export = MyLib
}
對于 ES Module 導出的包我們使用:
declare module 'my-lib' {
class MyLib {}
export default MyLib
// or other exorts
export class A {}
// 我們可以使用
// import MyLib, {A} from 'my-lib'
// const lib = new MyLib()
// const a = new A()
}
- 常量枚舉和普通枚舉區(qū)別
常量枚舉在編譯階段會被刪除
export const enum T{
a=1
}
T.a // 會被編譯為 1 /*a*/
enum T{
a=1
}
T.a 會被編譯為
var ITest;
(function (ITest) {
ITest[ITest["a"] = 1] = "a";
})(ITest || (ITest = {}));
ITest.a
- 一份較全的 tsconfig.json 配置(編譯上下文配置)
{
"compilerOptions": {
/* 基本選項 */
"target": "es5", // 指定 ECMAScript 目標版本: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
"module": "commonjs", // 指定使用模塊: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
"lib": [], // 指定要包含在編譯中的庫文件
"allowJs": true, // 允許編譯 javascript 文件
"checkJs": true, // 報告 javascript 文件中的錯誤
"jsx": "preserve", // 指定 jsx 代碼的生成: 'preserve', 'react-native', or 'react'
"declaration": true, // 生成相應的 '.d.ts' 文件
"sourceMap": true, // 生成相應的 '.map' 文件
"outFile": "./", // 將輸出文件合并為一個文件
"outDir": "./", // 指定輸出目錄
"rootDir": "./", // 用來控制輸出目錄結構 --outDir.
"removeComments": true, // 刪除編譯后的所有的注釋
"noEmit": true, // 不生成輸出文件
"importHelpers": true, // 從 tslib 導入輔助工具函數(shù)
"isolatedModules": true, // 將每個文件做為單獨的模塊 (與 'ts.transpileModule' 類似).
/* 嚴格的類型檢查選項 */
"strict": true, // 啟用所有嚴格類型檢查選項
"noImplicitAny": true, // 在表達式和聲明上有隱含的 any類型時報錯
"strictNullChecks": true, // 啟用嚴格的 null 檢查
"noImplicitThis": true, // 當 this 表達式值為 any 類型的時候,生成一個錯誤
"alwaysStrict": true, // 以嚴格模式檢查每個模塊泽示,并在每個文件里加入 'use strict'
/* 額外的檢查 */
"noUnusedLocals": true, // 有未使用的變量時缸血,拋出錯誤
"noUnusedParameters": true, // 有未使用的參數(shù)時蜜氨,拋出錯誤
"noImplicitReturns": true, // 并不是所有函數(shù)里的代碼都有返回值時,拋出錯誤
"noFallthroughCasesInSwitch": true, // 報告 switch 語句的 fallthrough 錯誤属百。(即记劝,不允許 switch 的 case 語句貫穿)
/* 模塊解析選項 */
"moduleResolution": "node", // 選擇模塊解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
"baseUrl": "./", // 用于解析非相對模塊名稱的基目錄
"paths": {}, // 模塊名到基于 baseUrl 的路徑映射的列表
"rootDirs": [], // 根文件夾列表,其組合內(nèi)容表示項目運行時的結構內(nèi)容
"typeRoots": [], // 包含類型聲明的文件列表
"types": [], // 需要包含的類型聲明文件名列表
"allowSyntheticDefaultImports": true, // 允許從沒有設置默認導出的模塊中默認導入族扰。
/* Source Map Options */
"sourceRoot": "./", // 指定調(diào)試器應該找到 TypeScript 文件而不是源文件的位置
"mapRoot": "./", // 指定調(diào)試器應該找到映射文件而不是生成文件的位置
"inlineSourceMap": true, // 生成單個 soucemaps 文件厌丑,而不是將 sourcemaps 生成不同的文件
"inlineSources": true, // 將代碼與 sourcemaps 生成到一個文件中,要求同時設置了 --inlineSourceMap 或 --sourceMap 屬性
/* 其他選項 */
"experimentalDecorators": true, // 啟用裝飾器
"emitDecoratorMetadata": true // 為裝飾器提供元數(shù)據(jù)的支持
/* 編譯文件選擇 */
"files":[],
"include":[],
"exclude":[],
}
}