本文主要向您展示“TypeScript数据类型中模板文字的示例分析”,简单易懂,组织清晰,希望能帮您解决疑惑。让边肖带领您学习和学习文章“TypeScript数据类型中模板文字的示例分析”。
模板字面量类型(Template Literal Types)
模板文字类型基于字符串文字类型,可以通过union类型扩展成多个字符串。
它们具有与JavaScript模板字符串相同的语法,但只能在类型操作中使用。当使用模板文字类型时,它将替换模板中的变量并返回一个新的字符串文字:
typeWorld=' world
type hello=` hello $ { World } `;
//类型问候语='helloworld '当模板中的变量是联合类型时,每个可能的文字字符串都将被表示为:
typeemailillcaleids=' welcome _ email ' | ' email _ heading ';
typeFooterLocaleIDs=' footer _ title ' | ' footer _ send off ';
typellocaleids=` $ { emaillcaleids | FooterLocaleIDs } _ id `;
//typealloclides=' Welcome _ email _ ID ' | ' email _ heading _ ID ' | ' footer _ title _ ID ' | ' footer _ sendoff _ ID '如果模板文字中的多个变量为联合类型,则结果会交叉相乘。例如,以下示例中有2 2 3个结果:
typellocaleids=` $ { emaillcaleids | FooterLocaleIDs } _ id `;
typeLang=' en ' | ' ja ' | ' pt
typellocalemessageds=` $ { Lang } _ $ { AllLocaleIDs } `;
//typelocalemessageds=' en _ welcome _ email _ ID ' | ' en _ email _ heading _ ID ' | ' en _ footer _ title _ ID ' | ' en _ footer _ sendoff _ ID ' | ' Ja _ welcome _ email _ ID ' | ' Ja _ footer _ title _ ID ' | ' Ja _ footer _ sendoff _ ID ' | ' PT _ welcome _ email _ ID ' | ' PT _ email _ heading _ ID ' | ' PT _ footer _ title _ ID '
00-1010模板文字最有用的地方是,您可以基于类型内部的信息定义一个新字符串。让我们举个例子:
有一个函数makeWatchedObject,它向传入的对象添加一个on方法。在JavaScript中,它的调用看起来像这样:makeWatchedObject(baseObject
),我们假设这个传入对象为:
const passedObject = { firstName: "Saoirse", lastName: "Ronan", age: 26, };
这个 on
方法会被添加到这个传入对象上,该方法接受两个参数,eventName
( string
类型) 和 callBack
(function
类型):
// 伪代码 const result = makeWatchedObject(baseObject); result.on(eventName, callBack);
我们希望 eventName
是这种形式:attributeInThePassedObject + "Changed"
,举个例子,passedObject
有一个属性 firstName
,对应产生的 eventName
为 firstNameChanged
,同理,lastName
对应的是 lastNameChanged
,age
对应的是 ageChanged
。
当这个 callBack
函数被调用的时候:
-
应该被传入与
attributeInThePassedObject
相同类型的值。比如passedObject
中,firstName
的值的类型为string
, 对应firstNameChanged
事件的回调函数,则接受传入一个string
类型的值。age
的值的类型为number
,对应ageChanged
事件的回调函数,则接受传入一个number
类型的值。 -
返回值类型为
void
类型。
on()
方法的签名最一开始是这样的:on(eventName: string, callBack: (newValue: any) => void)
。 使用这样的签名,我们是不能实现上面所说的这些约束的,这个时候就可以使用模板字面量:
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}!`); });
注意这个例子里,on
方法添加的事件名为 "firstNameChanged"
, 而不仅仅是 "firstName"
,而回调函数传入的值 newValue
,我们希望约束为 string
类型。我们先实现第一点。
在这个例子里,我们希望传入的事件名的类型,是对象属性名的联合,只是每个联合成员都还在最后拼接一个 Changed
字符,在 JavaScript 中,我们可以做这样一个计算:
Object.keys(passedObject).map(x => ${x}Changed)
模板字面量提供了一个相似的字符串操作:
type PropEventSource<Type> = { on(eventName: `${string & keyof Type}Changed`, callback: (newValue: any) => void): void; }; /// Create a "watched object" with an 'on' method /// so that you can watch for changes to properties. declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>;
注意,我们在这里例子中,模板字面量里我们写的是 string & keyof Type
,我们可不可以只写成 keyof Type
呢?如果我们这样写,会报错:
type PropEventSource<Type> = { on(eventName: `${keyof Type}Changed`, callback: (newValue: any) => void): void; }; // Type 'keyof Type' is not assignable to type 'string | number | bigint | boolean | null | undefined'. // Type 'string | number | symbol' is not assignable to type 'string | number | bigint | boolean | null | undefined'. // ...
从报错信息中,我们也可以看出报错原因,在 《TypeScript 系列之 Keyof 操作符》里,我们知道 keyof
操作符会返回 string | number | symbol
类型,但是模板字面量的变量要求的类型却是 string | number | bigint | boolean | null | undefined
,比较一下,多了一个 symbol 类型,所以其实我们也可以这样写:
type PropEventSource<Type> = { on(eventName: `${Exclude<keyof Type, symbol>}Changed`, callback: (newValue: any) => void): void; };
再或者这样写:
type PropEventSource<Type> = { on(eventName: `${Extract<keyof Type, string>}Changed`, callback: (newValue: any) => void): void; };
使用这种方式,在我们使用错误的事件名时,TypeScript 会给出报错:
const person = makeWatchedObject({ firstName: "Saoirse", lastName: "Ronan", age: 26 }); person.on("firstNameChanged", () => {}); // Prevent easy human error (using the key instead of the event name) person.on("firstName", () => {}); // Argument of type '"firstName"' is not assignable to parameter of type '"firstNameChanged" | "lastNameChanged" | "ageChanged"'. // It's typo-resistant person.on("frstNameChanged", () => {}); // Argument of type '"frstNameChanged"' is not assignable to parameter of type '"firstNameChanged" | "lastNameChanged" | "ageChanged"'.
模板字面量的推断(Inference with Template Literals)
现在我们来实现第二点,回调函数传入的值的类型与对应的属性值的类型相同。我们现在只是简单的对 callBack
的参数使用 any
类型。实现这个约束的关键在于借助泛型函数:
-
捕获泛型函数第一个参数的字面量,生成一个字面量类型
-
该字面量类型可以被对象属性构成的联合约束
-
对象属性的类型可以通过索引访问获取
-
应用此类型,确保回调函数的参数类型与对象属性的类型是同一个类型
type PropEventSource<Type> = { on<Key extends string & keyof Type> (eventName: `${Key}Changed`, callback: (newValue: Type[Key]) => void ): void; }; declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>; const person = makeWatchedObject({ firstName: "Saoirse", lastName: "Ronan", age: 26 }); person.on("firstNameChanged", newName => { // (parameter) newName: string console.log(`new name is ${newName.toUpperCase()}`); }); person.on("ageChanged", newAge => { // (parameter) newAge: number if (newAge < 0) { console.warn("warning! negative age"); } })
这里我们把 on
改成了一个泛型函数。
当一个用户调用的时候传入 "firstNameChanged"
,TypeScript 会尝试着推断 Key
正确的类型。它会匹配 key
和 "Changed"
前的字符串 ,然后推断出字符串 "firstName"
,然后再获取原始对象的 firstName
属性的类型,在这个例子中,就是 string
类型。
内置字符操作类型(Intrinsic String Manipulation Types)
TypeScript 的一些类型可以用于字符操作,这些类型处于性能的考虑被内置在编译器中,你不能在 .d.ts
文件里找到它们。
Uppercase<StringType>
把每个字符转为大写形式:
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"
Lowercase<StringType>
把每个字符转为小写形式:
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"
Capitalize<StringType>
把字符串的第一个字符转为大写形式:
type LowercaseGreeting = "hello, world"; type Greeting = Capitalize<LowercaseGreeting>; // type Greeting = "Hello, world"
Uncapitalize<StringType>
把字符串的第一个字符转换为小写形式:
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; }
以上是“TypeScript数据类型中模板字面量的示例分析”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/152464.html