您好,登錄后才能下訂單哦!
這篇文章主要介紹“TypeScript 4.1中的模板字面類型是什么”,在日常操作中,相信很多人在TypeScript 4.1中的模板字面類型是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”TypeScript 4.1中的模板字面類型是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
新的語言特性
模板字面類型
自 ES6 開始,我們就可以通過模板字面量(Template Literals)的特性,用反引號來書寫字符串,而不只是單引號或雙引號:
const message = `text`;
正如 Flavio Copes 所言,模板字面量提供了之前用引號寫的字符串所不具備的特性:
定義多行字符串非常方便
可以輕松地進行變量和表達式的插值
可以用模板標簽創建 DSL(Domain Specific Language,領域特定語言)
模板字面量類型和 JavaScript 中的模板字符串語法完全一致,只不過是用在類型定義里面:
type Entity = 'Invoice'; type Notification = `${Entity} saved`; // 等同于 // type Notification = 'Invoice saved'; type Viewport = 'md' | 'xs'; type Device = 'mobile' | 'desktop'; type Screen = `${Viewport | Device} screen`; // 等同于下面這一行 // type Screen = 'md screen' | 'xs screen' | 'mobile screen' | 'desktop screen';
當我們定義了一個具體的字面量類型時,TypeScript 會通過拼接內容的方式產生新的字符串字面量類型。
鍵值對類型中鍵的重新映射(Key Remapping)
映射類型可以基于任意鍵創建新的對象類型。 字符串字面量可以用作映射類型中的屬性名稱:
type Actions = { [K in 'showEdit' | 'showCopy' | 'showDelete']?: boolean; }; // 等同于 type Actions = { showEdit?: boolean, showCopy?: boolean, showDelete?: boolean };
如果你想創建新鍵或過濾掉鍵,TypeScript 4.1 允許你使用新的 as 子句重新映射映射類型中的鍵:
type MappedTypeWithNewKeys<T> = { [K in keyof T as NewKeyType]: T[K] }
TypeScript Remapping KeysThe new as clause lets you leverage features like template literal types to easily create new property names based on old ones. Keys can be filtered by producing never so that you don’t have to use an extra Omit helper type in some cases: 通過使用新的 as 子句,我們可以利用模板字面量類型之類的特性輕松地基于舊屬性創建新屬性名稱。我們可以通過輸出 never 來過濾鍵,這樣在某些情況下就不必使用額外的 Omit 輔助類型:
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>; // ^ = type LazyPerson = { // getName: () => string; // getAge: () => number; // getLocation: () => string; // } // 去掉 'kind' 屬性 type RemoveKindField<T> = { [K in keyof T as Exclude<K, "kind">]: T[K] }; interface Circle { kind: "circle"; radius: number; } type KindlessCircle = RemoveKindField<Circle>; // ^ = type KindlessCircle = { // radius: number; // }
TypeScript 利用帶有 as 子句的模板文字類型 (source)
JSX 工廠函數
JSX 代表 JavaScript XML,它允許我們使用 JavaScript 編寫 HTML 元素并將其放置在 DOM 中,而無需任何 createElement() 或 appendChild() 方法,例如:
const greeting = <h5>Yes I can do it!</h5>; ReactDOM.render(greeting, document.getElementById('root'));
TypeScript 4.1 通過編譯器選項 jsx 的兩個新選項支持 React 17 的 jsx 和 jsxs 工廠函數:
react-jsx
react-jsxdev
“這些選項分別用于生產和開發編譯。通常,一個選項可以擴展自另一個選項。” — TypeScript發版說明
以下是兩個用于生產和開發的 TypeScript 配置文檔的兩個示例:
// ./src/tsconfig.json { "compilerOptions": { "module": "esnext", "target": "es2015", "jsx": "react-jsx", "strict": true }, "include": ["./**/*"] }
開發配置:
// ./src/tsconfig.dev.json { "extends": "./tsconfig.json", "compilerOptions": { "jsx": "react-jsxdev" } }
如下圖所示,TypeScript 4.1 支持在像 React 這樣的 JSX 環境中進行類型檢查:
遞歸條件類型
另一個新增功能是遞歸條件類型,它允許它們在分支中引用自己,從而能夠更靈活地處理條件類型,使得編寫遞歸類型別名更加容易。下面是一個使用 Awaited 展開深層嵌套的 Promise 的示例:
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T; // 類似 `promise.then(...)`, 但是在類型上更加精確 declare function customThen<T, U>( p: Promise<T>, onFulfilled: (value: Awaited<T>) => U ): Promise<Awaited<U>>;
但是應當注意的是,TypeScript 需要更多時間來進行遞歸類型的類型檢查。Microsoft 警告,應以負責任的態度謹慎使用它們。
Checked indexed accesses 索引訪問檢查
_ TypeScript 中的索引簽名允許可以像下面的 Options 接口中那樣訪問任意命名的屬性:
interface Options { path: string; permissions: number; // Extra properties are caught by this index signature. // 額外的屬性將被這個 [propName: string]: string | number; } function checkOptions(opts: Options) { opts.path; // string opts.permissions; // number // 這些都可以!因為類型都是 string | number opts.yadda.toString(); opts["foo bar baz"].toString(); opts[Math.random()].toString(); }
在這里,我們看到不是 path 以及 permissions 的屬性應具有 string | number 類型:
TypeScript 4.1 提供了一個新的標志 --noUncheckedIndexedAccess,使得每次屬性訪問(如 opts.path)或索引訪問(如 opts [“ blabla”] )都可能未定義。這意味著如果我們需要訪問上一個示例中的 opts.path 之類的屬性,則必須檢查其是否存在或使用非 null 斷言運算符(后綴 ! 字符):
function checkOptions(opts: Options) { opts.path; // string opts.permissions; // number // 以下代碼在 noUncheckedIndexedAccess 開啟時是非法的 opts.yadda.toString(); opts["foo bar baz"].toString(); opts[Math.random()].toString(); // 檢查屬性是否真的存在 if (opts.yadda) { console.log(opts.yadda.toString()); } // 直接使用非空斷言操作符 opts.yadda!.toString(); }
--noUncheckedIndexedAccess 標志對于捕獲很多錯誤很有用,但是對于很多代碼來說可能很嘈雜。 這就是為什么 --strict 開關不會自動啟用它的原因。
不需要 baseUrl 指定路徑
在 TypeScript 4.1 之前,要能夠使用 tsconfig.json 文件中的 paths,必須聲明 baseUrl 參數。 在新版本中,可以在不帶 paths 選項的情況下指定 baseUrl。 這解決了自動導入中路徑不暢的問題。
{ "compilerOptions": { "baseUrl": "./src", "paths": { "@shared": ["@shared/"] // This mapping is relative to "baseUrl" } } }
checkJs 默認打開 allowJs
如果您有一個 JavaScript 項目,正在其中使用 checkJs 選項檢查 .js 文件中的錯誤,則還應該聲明 allowJs 以允許編譯 JavaScript 文件。而 TypeScript 4.1 中,默認情況下 checkJs 意味著 allowJs:
{ compilerOptions: { allowJs: true, checkJs: true } }
JSDoc @see 標簽的編輯器支持
在編輯器中使用 TypeScript 時,現在對 JSDoc 標簽 @see 有了更好的支持,這將改善TypeScript 4.1的可用性:
// @filename: first.ts export class C {} // @filename: main.ts import * as first from "./first"; /** * @see first.C */ function related() {}
不兼容改變
lib.d.ts 變動
結構和 DOM 的環境聲明,使您可以輕松地開始編寫經過類型檢查的 JavaScript 代碼。
該文件自動包含在 TypeScript 項目的編譯上下文中。 您可以通過指定 --noLib 編譯器命令行標志或在 tsconfig.json 中配置 noLib 為 true 來排除它。
在 TypeScript 4.1 中,由于 DOM 類型是自動生成的,lib.d.ts 可能具有一組變動的 API,例如,從 ES2016 中刪除的 Reflect.enumerate。
abstract 成員不能被標記為 async
在另一個重大更改中,標記為 abstract 的成員不能被再標記為 async。 因此,要修復您的代碼,必須刪除 async 關鍵字:
abstract class MyClass { // 在 TypeScript 4.1 中必須刪除 async abstract async create(): Promise<string>; }
any/unknown 向外傳播
在 TypeScript 4.1 之前,對于像 foo && somethingElse 這樣的表達式, foo 的類型是 any 或 unknown。 整個表達式的類型將是 somethingElse 的類型,在以下示例中就是 {someProp:string} :
declare let foo: unknown; declare let somethingElse: { someProp: string }; let x = foo && somethingElse;
在 TypeScript 4.1 中, any 和 unknown 都將向外傳播,而不是在右側傳播。通常,這個變更合適的解決方法是從 foo && someExpression 切換到 !!foo && someExpression。
注意:雙重感嘆號(!!)是將變量強制轉換為布爾值(真或假)的一種簡便方法。
Promise 中 resolve 的參數不再是可選類型
Promise 中 resolve 的參數不再是可選的,例如下面的代碼:
new Promise((resolve) => { doSomethingAsync(() => { doSomething(); resolve(); }); });
這段代碼在 TypeScript 4.1 中編譯會報錯:
resolve() ~~~~~~~~~ error TS2554: Expected 1 arguments, but got 0. An argument for 'value' was not provided.
要解決這個問題,必須在 Promise 中給 resolve 提供至少一個值,否則,在確實需要不帶參數的情況下調用 resolve() 的情況下,必須使用顯式的 void 泛型類型參數聲明 Promise:
new Promise<void>((resolve) => { doSomethingAsync(() => { doSomething(); resolve(); }); });
條件展開將會創建可選屬性
在 JavaScript 中,展開運算符 { ...files } 不會作用于假值,例如 files 為 null 或者 undefined。
在以下使用條件傳播的示例中,如果定義了 file,則將傳播 file.owner 的屬性。否則,不會將任何屬性傳播到返回的對象中:
function getOwner(file?: File) { return { ...file?.owner, defaultUserId: 123, }; }
在TypeScript 4.1之前, getOwner 返回基于每個展開對象的聯合類型:
{ x: number } | { x: number, name: string, age: number, location: string }
如果定義了 file,則會擁有來自Person(所有者的類型)的所有屬性。
否則,結果中一個都不會展示
但是事實證明,這樣的代價最終會變得非常高昂,而且通常無濟于事。在單個對象中存在數百個展開對象,每個展開對象都可能增加數百或數千個屬性。 為了更好的性能,在TypeScript 4.1中,返回的類型有時使用全部可選屬性:
{ x: number; name?: string; age?: number; location?: string; }
不匹配的參數將不再關聯
過去,彼此不對應的參數在 TypeScript 中通過將它們與 any 類型關聯而彼此關聯。
在下面的重載示例(為同一功能提供多種功能類型)中, pickCard 函數將根據用戶傳入的內容返回兩個不同的內容。如果用戶傳入表示 deck 的對象,則該函數將選擇 card。 如果用戶選擇了 card,他們將得到他們選擇的 card:
let suits = ["hearts", "spades", "clubs", "diamonds"]; function pickCard(x: { suit: string; card: number }[]): number; function pickCard(x: number): { suit: string; card: number }; function pickCard(x: any): any { // Check to see if we're working with an object/array // if so, they gave us the deck and we'll pick the card if (typeof x == "object") { let pickedCard = Math.floor(Math.random() * x.length); return pickedCard; } // Otherwise just let them pick the card else if (typeof x == "number") { let pickedSuit = Math.floor(x / 13); return { suit: suits[pickedSuit], card: x % 13 }; } } let myDeck = [ { suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }, { suit: "hearts", card: 4 }, ]; let pickedCard1 = myDeck[pickCard(myDeck)]; alert("card: " + pickedCard1.card + " of " + pickedCard1.suit); let pickedCard2 = pickCard(15); alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);
使用 TypeScript 4.1,某些情況下賦值將會失敗,而某些情況下的重載解析則將失敗。解決方法是,最好使用類型斷言來避免錯誤。
到此,關于“TypeScript 4.1中的模板字面類型是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。