ホームページ  >  記事  >  ウェブフロントエンド  >  TypeScript データ型のテンプレート リテラルを理解する

TypeScript データ型のテンプレート リテラルを理解する

青灯夜游
青灯夜游転載
2021-12-17 10:40:452210ブラウズ

TypeScript チームは TypeScript 4.1 をリリースしました。これには、強力なテンプレート リテラル型、マップされた型のキーの再マッピング、および再帰的な条件付き型が含まれています。次の記事では、TypeScript のテンプレート リテラル タイプについて説明します。お役に立てば幸いです。

TypeScript データ型のテンプレート リテラルを理解する

テンプレート リテラル タイプ

テンプレート リテラル タイプは文字列リテラル タイプに基づいており、共用体タイプを介して複数のタイプに拡張できます。

これらの構文は JavaScript テンプレート文字列と同じですが、型操作でのみ使用できます。テンプレート リテラル タイプを使用すると、テンプレート内の変数が置き換えられ、新しい文字列リテラルが返されます:

type World = "world";
 
type Greeting = `hello ${World}`;
// type Greeting = "hello world"

テンプレート内の変数が共用体タイプの場合、考えられるすべての文字列リテラル数量が表されます:

type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";
 
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
// type AllLocaleIDs = "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"

テンプレート リテラル内の複数の変数が共用体型の場合、結果は相互乗算されます。たとえば、次の例には 2 2 3 があり、合計 12 種類の型があります。

type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
type Lang = "en" | "ja" | "pt";
 
type LocaleMessageIDs = `${Lang}_${AllLocaleIDs}`;
// 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"

実際に非常に長い文字列共用体型の場合は、事前に生成することをお勧めしますが、これは短い状況に適しています。

型の文字列共用体

テンプレート リテラルの最も便利な点は、型の内部情報に基づいて新しい文字列を定義できることです。例を見てみましょう:

関数 makeWatchedObject があり、渡されたオブジェクトに on メソッドを追加します。 JavaScript では、その呼び出しは次のようになります: makeWatchedObject(baseObject)、受信オブジェクトは次のとおりであると想定します:

const passedObject = {
  firstName: "Saoirse",
  lastName: "Ronan",
  age: 26,
};

この on メソッドはこの受信時に追加されますオブジェクトの場合、このメソッドは eventName (string タイプ) と callBack (function タイプ) の 2 つのパラメータを受け入れます。

#eventName

が次の形式であることを望みます: attributeInThePassedObject "Changed"。たとえば、passedObject には属性 firstName があり、対応する生成された eventNamefirstNameChanged です。同様に、lastNamelastNameChanged に対応し、ageageChanged# に対応します##。 この callBack

関数が呼び出されるとき:

attributeInThePassedObject
    と同じタイプの値で渡される必要があります。たとえば、
  • passedObject では、firstName の値の型は string であり、firstNameChanged イベントに対応するコールバック関数は # を受け入れます。 # 渡されます。 #string 型の値。 age の値の型は number であり、ageChanged イベントに対応するコールバック関数は型 number の値を受け入れます。 戻り値の型は void 型です。
  • on()
  • メソッドのシグネチャは、最初は次のようになっていました:
on(eventName: string, callBack: (newValue: any) => void)

。このような署名を使用すると、上記の制約を実装できません。この時点では、テンプレート リテラルを使用できます: <pre class="brush:js;toolbar:false;">// 伪代码 const result = makeWatchedObject(baseObject); result.on(eventName, callBack);</pre>この例では、on メソッドによって追加されたイベント名が追加されていることに注意してください。は

"firstName"

ではなく "firstNameChanged" であり、コールバック関数によって渡される値は newValue です。制約は にする必要があります。 string タイプ。まずは最初のポイントを実装しましょう。 この例では、渡されるイベント名の型がオブジェクト プロパティ名の結合であることを望みますが、各結合メンバーは最後に Changed 文字で結合されます。 JavaScript では、このような計算を行うことができます:

const person = makeWatchedObject({
  firstName: "Saoirse",
  lastName: "Ronan",
  age: 26,
});
 
// makeWatchedObject has added `on` to the anonymous Object
person.on("firstNameChanged", (newValue) => {
  console.log(`firstName was changed to ${newValue}!`);
});

テンプレート リテラルでも同様の文字列操作が提供されます: <pre class="brush:js;toolbar:false;">Object.keys(passedObject).map(x =&gt; ${x}Changed)</pre> ここの例では、テンプレート リテラルに It's

と記述していることに注意してください。 string & keyof Type

ですが、単に

keyof Type

と書いてもいいでしょうか?このように書くと、エラーが報告されます: <pre class="brush:js;toolbar:false;">type PropEventSource&lt;Type&gt; = { on(eventName: `${string &amp; keyof Type}Changed`, callback: (newValue: any) =&gt; void): void; }; /// Create a &quot;watched object&quot; with an &amp;#39;on&amp;#39; method /// so that you can watch for changes to properties. declare function makeWatchedObject&lt;Type&gt;(obj: Type): Type &amp; PropEventSource&lt;Type&gt;;</pre> エラーメッセージから、エラーの理由もわかります。「TypeScript シリーズの Keyof 演算子」では、 がkeyof 演算子 型

string |number |symbol

が返されますが、テンプレート リテラル変数に必要な型は string |number | bigint | boolean | null | unknown です。比較のために、もう 1 つのシンボル タイプがあるので、実際には次のように書くこともできます: <pre class="brush:js;toolbar:false;">type PropEventSource&lt;Type&gt; = { on(eventName: `${keyof Type}Changed`, callback: (newValue: any) =&gt; void): void; }; // Type &amp;#39;keyof Type&amp;#39; is not assignable to type &amp;#39;string | number | bigint | boolean | null | undefined&amp;#39;. // Type &amp;#39;string | number | symbol&amp;#39; is not assignable to type &amp;#39;string | number | bigint | boolean | null | undefined&amp;#39;. // ...</pre> または次のように書くこともできます: <pre class="brush:js;toolbar:false;">type PropEventSource&lt;Type&gt; = { on(eventName: `${Exclude&lt;keyof Type, symbol&gt;}Changed`, callback: (newValue: any) =&gt; void): void; };</pre> このメソッドを使用すると、TypeScript はエラーを返します。間違ったイベント名を使用してください:

type PropEventSource<Type> = {
     on(eventName: `${Extract<keyof Type, string>}Changed`, callback: (newValue: any) => void): void;
};

テンプレート リテラルによる推論

次に 2 番目のポイント、コールバック関数によって渡される値の型と対応する属性を実装しましょう。値は同じ型です。ここでは、callBack のパラメータに

any

タイプを単純に使用しています。この制約を実現する鍵は、汎用関数を使用することです: <ul style="list-style-type: disc;"> <li><p>捕获泛型函数第一个参数的字面量,生成一个字面量类型</p></li> <li><p>该字面量类型可以被对象属性构成的联合约束</p></li> <li><p>对象属性的类型可以通过索引访问获取</p></li> <li><p>应用此类型,确保回调函数的参数类型与对象属性的类型是同一个类型</p></li> </ul><pre class="brush:js;toolbar:false;">type PropEventSource&lt;Type&gt; = { on&lt;Key extends string &amp; keyof Type&gt; (eventName: `${Key}Changed`, callback: (newValue: Type[Key]) =&gt; void ): void; }; declare function makeWatchedObject&lt;Type&gt;(obj: Type): Type &amp; PropEventSource&lt;Type&gt;; const person = makeWatchedObject({ firstName: &quot;Saoirse&quot;, lastName: &quot;Ronan&quot;, age: 26 }); person.on(&quot;firstNameChanged&quot;, newName =&gt; { // (parameter) newName: string console.log(`new name is ${newName.toUpperCase()}`); }); person.on(&quot;ageChanged&quot;, newAge =&gt; { // (parameter) newAge: number if (newAge &lt; 0) { console.warn(&quot;warning! negative age&quot;); } })</pre><p>这里我们把 <code>on 改成了一个泛型函数。

当一个用户调用的时候传入 "firstNameChanged",TypeScript 会尝试着推断 Key 正确的类型。它会匹配 key"Changed" 前的字符串 ,然后推断出字符串 "firstName" ,然后再获取原始对象的 firstName 属性的类型,在这个例子中,就是 string 类型。

内置字符操作类型(Intrinsic String Manipulation Types)

TypeScript 的一些类型可以用于字符操作,这些类型处于性能的考虑被内置在编译器中,你不能在 .d.ts 文件里找到它们。

Uppercasea24091e5aa4cbf3564292ba7dbe442c1

把每个字符转为大写形式:

type Greeting = "Hello, world"
type ShoutyGreeting = Uppercase<Greeting>        
// type ShoutyGreeting = "HELLO, WORLD"
 
type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`
type MainID = ASCIICacheKey<"my_app">
// type MainID = "ID-MY_APP"

Lowercasea24091e5aa4cbf3564292ba7dbe442c1

把每个字符转为小写形式:

type Greeting = "Hello, world"
type QuietGreeting = Lowercase<Greeting>       
// type QuietGreeting = "hello, world"
 
type ASCIICacheKey<Str extends string> = `id-${Lowercase<Str>}`
type MainID = ASCIICacheKey<"MY_APP">    
// type MainID = "id-my_app"

Capitalizea24091e5aa4cbf3564292ba7dbe442c1

把字符串的第一个字符转为大写形式:

type LowercaseGreeting = "hello, world";
type Greeting = Capitalize<LowercaseGreeting>;
// type Greeting = "Hello, world"

Uncapitalizea24091e5aa4cbf3564292ba7dbe442c1

把字符串的第一个字符转换为小写形式:

type UppercaseGreeting = "HELLO WORLD";
type UncomfortableGreeting = Uncapitalize<UppercaseGreeting>;           
// type UncomfortableGreeting = "hELLO WORLD"

字符操作类型的技术细节

从 TypeScript 4.1 起,这些内置函数会直接使用 JavaScript 字符串运行时函数,而不是本地化识别 (locale aware)。

function applyStringMapping(symbol: Symbol, str: string) {
    switch (intrinsicTypeKinds.get(symbol.escapedName as string)) {
        case IntrinsicTypeKind.Uppercase: return str.toUpperCase();
        case IntrinsicTypeKind.Lowercase: return str.toLowerCase();
        case IntrinsicTypeKind.Capitalize: return str.charAt(0).toUpperCase() + str.slice(1);
        case IntrinsicTypeKind.Uncapitalize: return str.charAt(0).toLowerCase() + str.slice(1);
    }
    return str;
}

【相关推荐:javascript学习教程

以上がTypeScript データ型のテンプレート リテラルを理解するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。