在打字稿手冊模板文字型別中,它說:
對于模板文字中的每個插值位置,聯合交叉相乘:
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`; type Lang = "en" | "ja" | "pt"; type LocaleMessageIDs = `${Lang}_${AllLocaleIDs}`;
然后它說:
我們通常建議人們對大型字串聯合使用提前生成,但這在較小的情況下很有用。
但什么是“超前一代”?最后一段是什么意思?因為他沒有舉個例子,所以我不太明白。
uj5u.com熱心網友回復:
為了獲得類似權威答案的內容,我查找了介紹此檔案的提交,將其追溯到具有類似措辭的TypeScript 4.1 發行說明的提交:
當您需要大量字串時,您應該考慮提前自動生成它們以節省每次型別檢查的作業
這兩個提交都是由@orta 撰寫的。我在 TypeScript Discord 上問他最初來自哪里,它基本上來自@DanielRosenwasser 撰寫的 TS 開發博客,但 @orta 也編輯了文本。
因此,“提前生成”是用您選擇的語言撰寫一些代碼的行為(這可能是 TypeScript,但不一定是),當您提前運行它時會生成您想要的 TypeScript 代碼一次,一次。
因此,不是直接撰寫執行 X 的代碼,而是撰寫撰寫執行 X 的代碼的代碼。
這里的建議是,不要撰寫以下 TypeScript 代碼,
type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
type Lang = "en" | "ja" | "pt";
type LocaleMessageIDs = `${Lang}_${AllLocaleIDs}`;
在編譯器必須評估LocaleMessageIDs聯合本身的情況下,您離開 TypeScript IDE 并轉到其他環境(例如不同的 TypeScript IDE)并撰寫以下代碼:
const locales = ["welcome_email", "email_heading", "footer_title", "footer_sendoff"];
const langs = ["en", "ja", "pt"];
console.log(
"type LocaleMessageIDs = "
langs.map(lang =>
locales.map(locale =>
"\"" lang "_" locale "_id\""
).join(" | ")
).join(" |\n ") ";"
);
運行此代碼時,控制臺會記錄以下內容:
type LocaleMessageIDs = "en_welcome_email_id" | "en_email_heading_id" | "en_footer_title_id" | "en_footer_sendoff_id" |
"ja_welcome_email_id" | "ja_email_heading_id" | "ja_footer_title_id" | "ja_footer_sendoff_id" |
"pt_welcome_email_id" | "pt_email_heading_id" | "pt_footer_title_id" | "pt_footer_sendoff_id";
您可以將其復制并粘貼到原始 TypeScript IDE 中。
Playground 鏈接到代碼
在這兩種情況下,您最終都會得到一個LocaleMessageIDs型別,它是相同的十二個字串文字型別的聯合。但在第一種情況下,TypeScript 編譯器通過處理模板文字型別來評估聯合,而在第二種情況下,TypeScript 編譯器只需要直接決議字串文字。
對于簡單的小型聯合,例如LocaleMessageID進行任何型別的代碼生成都是非常愚蠢的。但是很容易將其更改為生成聯合的東西,這些聯合非常復雜,足以計算出編譯器性能嚴重下降。
這是一個名為 的型別的故意次優實作PalindromicFourteenBitString,任何字串只由"0"和組成,"1"向前和向后相同:
type Binary = '1' | '0';
type BinaryLength<N extends number, L extends 0[] = [], O extends string = ""> =
N extends L['length'] ? O : BinaryLength<N, [0, ...L], `${O}${Binary}`>;
type Reverse<T extends string, C extends string = ""> =
T extends `${infer F}${infer R}` ?
Reverse<R, `${F}${C}`> : C;
type PalindromicFourteenBitString = BinaryLength<14> extends infer B ?
B extends Reverse<Extract<B, string>> ? B : never : never;
這是一個次優的實作,因為我們生成每個長度為 14 的二進制字串并過濾掉任何不是回文的字串。有一個更好的方法來做到這一點(生成長度為 7 的字串并鏡像每個字串),但關鍵是想出一些需要一些計算來評估的東西。
無論如何,當您去測驗它時,您可能會注意到編譯器指出錯誤所在的行的速度很慢,如果您查看計算機的 CPU,它可能會非常努力地作業:
let b: PalindromicFourteenBitString; // ?? ??????
b = "10010011001001"; // okay
b = "10001011001001"; // error
b = "00110011001100"; // okay
Playground 鏈接到代碼
將此與執行代碼生成時發生的情況進行比較:
const palindromicFourteenBitStrings =
Array.from({ length: 1 << 14 }, (_, i) => i.toString(2).padStart(14, "0"))
.filter(s => s === s.split("").reverse().join(""));
console.log(
"type PalindromicFourteenBitString =\n "
palindromicFourteenBitStrings.map((x, i) => '"' x '"'
(i % 8 == 7 ? "\n " : "")).join(" | "));
如果您運行它,它會輸出以下內容(并且很快):
type PalindromicFourteenBitString =
"00000000000000" | "00000011000000" | "00000100100000" | "00000111100000" | "00001000010000" | "00001011010000" | "00001100110000" | "00001111110000"
| "00010000001000" | "00010011001000" | "00010100101000" | "00010111101000" | "00011000011000" | "00011011011000" | "00011100111000" | "00011111111000"
| "00100000000100" | "00100011000100" | "00100100100100" | "00100111100100" | "00101000010100" | "00101011010100" | "00101100110100" | "00101111110100"
| "00110000001100" | "00110011001100" | "00110100101100" | "00110111101100" | "00111000011100" | "00111011011100" | "00111100111100" | "00111111111100"
| "01000000000010" | "01000011000010" | "01000100100010" | "01000111100010" | "01001000010010" | "01001011010010" | "01001100110010" | "01001111110010"
| "01010000001010" | "01010011001010" | "01010100101010" | "01010111101010" | "01011000011010" | "01011011011010" | "01011100111010" | "01011111111010"
| "01100000000110" | "01100011000110" | "01100100100110" | "01100111100110" | "01101000010110" | "01101011010110" | "01101100110110" | "01101111110110"
| "01110000001110" | "01110011001110" | "01110100101110" | "01110111101110" | "01111000011110" | "01111011011110" | "01111100111110" | "01111111111110"
| "10000000000001" | "10000011000001" | "10000100100001" | "10000111100001" | "10001000010001" | "10001011010001" | "10001100110001" | "10001111110001"
| "10010000001001" | "10010011001001" | "10010100101001" | "10010111101001" | "10011000011001" | "10011011011001" | "10011100111001" | "10011111111001"
| "10100000000101" | "10100011000101" | "10100100100101" | "10100111100101" | "10101000010101" | "10101011010101" | "10101100110101" | "10101111110101"
| "10110000001101" | "10110011001101" | "10110100101101" | "10110111101101" | "10111000011101" | "10111011011101" | "10111100111101" | "10111111111101"
| "11000000000011" | "11000011000011" | "11000100100011" | "11000111100011" | "11001000010011" | "11001011010011" | "11001100110011" | "11001111110011"
| "11010000001011" | "11010011001011" | "11010100101011" | "11010111101011" | "11011000011011" | "11011011011011" | "11011100111011" | "11011111111011"
| "11100000000111" | "11100011000111" | "11100100100111" | "11100111100111" | "11101000010111" | "11101011010111" | "11101100110111" | "11101111110111"
| "11110000001111" | "11110011001111" | "11110100101111" | "11110111101111" | "11111000011111" | "11111011011111" | "11111100111111" | "11111111111111";
如果你把輸出作為您的TS的源代碼及測驗的是,該編譯器的性能是非常快的,而且也沒有CPU秒殺發言:
let b: PalindromicFourteenBitString; ?? ??????
b = "10010011001001"; // okay
b = "10001011001001"; // error
b = "00110011001100"; // okay
Playground 鏈接到代碼
因此,這是一個示例,在此示例中,您將有更好的體驗來提前計算聯合,而不是要求 TypeScript 編譯器通過模板文字型別操作來完成。
當我問他那行檔案是什么意思時,@orta 詳細說明不應在模板字串文字型別中撰寫大型別函式,例如將GraphQL 查詢字串轉換為相應的型別。如果你想撰寫代碼來做到這一點,你應該用其他語言撰寫它并從它的輸出中生成 TypeScript 型別。
旁白:濫用編譯器來評估型別比代碼生成更糟糕的其他壞主意
- 小于或等于某個值的素數元組
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/373503.html
標籤:打字稿
