search
HomeWeb Front-endJS TutorialUnderstanding template literals in TypeScript data types

Understanding template literals in TypeScript data types

Dec 17, 2021 am 10:40 AM
typescripttype of data

The TypeScript team has released TypeScript 4.1, which includes powerful template literal types, key remapping of mapped types, and recursive conditional types. The following article will take you through the template literal types in TypeScript. I hope it will be helpful to you!

Understanding template literals in TypeScript data types

Template Literal Types

The template literal type is based on the string literal type and can be expanded into multiple types through union types. string.

They have the same syntax as JavaScript template strings, but can only be used in type operations. When a template literal type is used, it replaces the variable in the template and returns a new string literal:

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

When the variable in the template is a union type, every possible string literal Quantities will be represented:

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"

If multiple variables in the template literal are of union type, the results will be cross-multiplied. For example, the following example has 2 2 3, a total of 12 types Result:

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"

If it is really a very long string union type, it is recommended to generate it in advance. This is still suitable for shorter situations.

String Unions in Types

The most useful thing about template literals is that you can define a new string based on the internal information of a type, so that Let's take an example:

There is such a function makeWatchedObject, which adds an on method to the passed in object. In JavaScript, its call looks like this: makeWatchedObject(baseObject), we assume that the incoming object is:

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

This on method will be added On this incoming object, this method accepts two parameters, eventName (string type) and callBack (function type):

// 伪代码
const result = makeWatchedObject(baseObject);
result.on(eventName, callBack);

We hope that eventName is in this form: attributeInThePassedObject "Changed", for example, passedObject has an attribute firstName , the corresponding generated eventName is firstNameChanged, similarly, lastName corresponds to lastNameChanged, age corresponds to ageChanged.

When this callBack function is called:

  • should be passed in the same type of value as attributeInThePassedObject. For example, in passedObject, the value type of firstName is string, and the callback function corresponding to the firstNameChanged event accepts a ## passed in. #string Value of type. The value type of age is number, and the callback function corresponding to the ageChanged event accepts a value of type number.
  • The return value type is
  • void type.

on() The signature of the method initially looked like this: on(eventName: string, callBack: (newValue: any) => void). Using such a signature, we cannot implement the constraints mentioned above. At this time, we can use template literals:

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}!`);
});

Note that in this example, the event name added by the

on method is "firstNameChanged", not just "firstName", and the value passed in by the callback function is newValue, we want the constraint to be string type. Let’s implement the first point first.

In this example, we hope that the type of the event name passed in is a union of object property names, but each union member is spliced ​​with a

Changed character at the end, in JavaScript , we can do such a calculation:

Object.keys(passedObject).map(x => ${x}Changed)

Template literals provide a similar string operation:

type PropEventSource<Type> = {
    on(eventName: `${string & keyof Type}Changed`, callback: (newValue: any) => void): void;
};
 
/// Create a "watched object" with an &#39;on&#39; method
/// so that you can watch for changes to properties.

declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>;

Note that in our example here, we wrote in the template literal It's

string & keyof Type, can we just write it as keyof Type? If we write like this, an error will be reported:

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

// Type &#39;keyof Type&#39; is not assignable to type &#39;string | number | bigint | boolean | null | undefined&#39;.
// Type &#39;string | number | symbol&#39; is not assignable to type &#39;string | number | bigint | boolean | null | undefined&#39;.
// ...

From the error message, we can also see the reason for the error. In "Keyof Operator of TypeScript Series", we know that the

keyof operator The type string | number | symbol will be returned, but the type required for the template literal variable is string | number | bigint | boolean | null | undefined. For comparison, there is one more symbol type, so in fact we can also write like this:

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

Or write like this:

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

Using this method, TypeScript will give an error when we use the wrong event name:

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 &#39;"firstName"&#39; is not assignable to parameter of type &#39;"firstNameChanged" | "lastNameChanged" | "ageChanged"&#39;.
 
// It&#39;s typo-resistant
person.on("frstNameChanged", () => {});
// Argument of type &#39;"frstNameChanged"&#39; is not assignable to parameter of type &#39;"firstNameChanged" | "lastNameChanged" | "ageChanged"&#39;.

Inference with Template Literals

Now let’s implement the second point, the type of value passed in by the callback function and the corresponding attributes The values ​​are of the same type. We are now simply using the

any type for the parameters of callBack. The key to realizing this constraint is to use generic functions:

  • 捕获泛型函数第一个参数的字面量,生成一个字面量类型

  • 该字面量类型可以被对象属性构成的联合约束

  • 对象属性的类型可以通过索引访问获取

  • 应用此类型,确保回调函数的参数类型与对象属性的类型是同一个类型

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

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

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

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

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

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

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

Uncapitalize

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

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学习教程

The above is the detailed content of Understanding template literals in TypeScript data types. For more information, please follow other related articles on the PHP Chinese website!

Statement
This article is reproduced at:segmentfault. If there is any infringement, please contact admin@php.cn delete
Javascript Data Types : Is there any difference between Browser and NodeJs?Javascript Data Types : Is there any difference between Browser and NodeJs?May 14, 2025 am 12:15 AM

JavaScript core data types are consistent in browsers and Node.js, but are handled differently from the extra types. 1) The global object is window in the browser and global in Node.js. 2) Node.js' unique Buffer object, used to process binary data. 3) There are also differences in performance and time processing, and the code needs to be adjusted according to the environment.

JavaScript Comments: A Guide to Using // and /* */JavaScript Comments: A Guide to Using // and /* */May 13, 2025 pm 03:49 PM

JavaScriptusestwotypesofcomments:single-line(//)andmulti-line(//).1)Use//forquicknotesorsingle-lineexplanations.2)Use//forlongerexplanationsorcommentingoutblocksofcode.Commentsshouldexplainthe'why',notthe'what',andbeplacedabovetherelevantcodeforclari

Python vs. JavaScript: A Comparative Analysis for DevelopersPython vs. JavaScript: A Comparative Analysis for DevelopersMay 09, 2025 am 12:22 AM

The main difference between Python and JavaScript is the type system and application scenarios. 1. Python uses dynamic types, suitable for scientific computing and data analysis. 2. JavaScript adopts weak types and is widely used in front-end and full-stack development. The two have their own advantages in asynchronous programming and performance optimization, and should be decided according to project requirements when choosing.

Python vs. JavaScript: Choosing the Right Tool for the JobPython vs. JavaScript: Choosing the Right Tool for the JobMay 08, 2025 am 12:10 AM

Whether to choose Python or JavaScript depends on the project type: 1) Choose Python for data science and automation tasks; 2) Choose JavaScript for front-end and full-stack development. Python is favored for its powerful library in data processing and automation, while JavaScript is indispensable for its advantages in web interaction and full-stack development.

Python and JavaScript: Understanding the Strengths of EachPython and JavaScript: Understanding the Strengths of EachMay 06, 2025 am 12:15 AM

Python and JavaScript each have their own advantages, and the choice depends on project needs and personal preferences. 1. Python is easy to learn, with concise syntax, suitable for data science and back-end development, but has a slow execution speed. 2. JavaScript is everywhere in front-end development and has strong asynchronous programming capabilities. Node.js makes it suitable for full-stack development, but the syntax may be complex and error-prone.

JavaScript's Core: Is It Built on C or C  ?JavaScript's Core: Is It Built on C or C ?May 05, 2025 am 12:07 AM

JavaScriptisnotbuiltonCorC ;it'saninterpretedlanguagethatrunsonenginesoftenwritteninC .1)JavaScriptwasdesignedasalightweight,interpretedlanguageforwebbrowsers.2)EnginesevolvedfromsimpleinterpreterstoJITcompilers,typicallyinC ,improvingperformance.

JavaScript Applications: From Front-End to Back-EndJavaScript Applications: From Front-End to Back-EndMay 04, 2025 am 12:12 AM

JavaScript can be used for front-end and back-end development. The front-end enhances the user experience through DOM operations, and the back-end handles server tasks through Node.js. 1. Front-end example: Change the content of the web page text. 2. Backend example: Create a Node.js server.

Python vs. JavaScript: Which Language Should You Learn?Python vs. JavaScript: Which Language Should You Learn?May 03, 2025 am 12:10 AM

Choosing Python or JavaScript should be based on career development, learning curve and ecosystem: 1) Career development: Python is suitable for data science and back-end development, while JavaScript is suitable for front-end and full-stack development. 2) Learning curve: Python syntax is concise and suitable for beginners; JavaScript syntax is flexible. 3) Ecosystem: Python has rich scientific computing libraries, and JavaScript has a powerful front-end framework.

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

mPDF

mPDF

mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

WebStorm Mac version

WebStorm Mac version

Useful JavaScript development tools

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver Mac version

Dreamweaver Mac version

Visual web development tools