Understanding template literals in TypeScript data types
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!
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, inpassedObject
, the value type offirstName
isstring
, and the callback function corresponding to thefirstNameChanged
event accepts a ## passed in. #stringValue of type. The value type of
ageis
number, and the callback function corresponding to the
ageChangedevent 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.
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 'on' 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 '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'. // ...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 '"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
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 theany 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!

JavaScript's application in the real world includes front-end and back-end development. 1) Display front-end applications by building a TODO list application, involving DOM operations and event processing. 2) Build RESTfulAPI through Node.js and Express to demonstrate back-end applications.

The main uses of JavaScript in web development include client interaction, form verification and asynchronous communication. 1) Dynamic content update and user interaction through DOM operations; 2) Client verification is carried out before the user submits data to improve the user experience; 3) Refreshless communication with the server is achieved through AJAX technology.

Understanding how JavaScript engine works internally is important to developers because it helps write more efficient code and understand performance bottlenecks and optimization strategies. 1) The engine's workflow includes three stages: parsing, compiling and execution; 2) During the execution process, the engine will perform dynamic optimization, such as inline cache and hidden classes; 3) Best practices include avoiding global variables, optimizing loops, using const and lets, and avoiding excessive use of closures.

Python is more suitable for beginners, with a smooth learning curve and concise syntax; JavaScript is suitable for front-end development, with a steep learning curve and flexible syntax. 1. Python syntax is intuitive and suitable for data science and back-end development. 2. JavaScript is flexible and widely used in front-end and server-side programming.

Python and JavaScript have their own advantages and disadvantages in terms of community, libraries and resources. 1) The Python community is friendly and suitable for beginners, but the front-end development resources are not as rich as JavaScript. 2) Python is powerful in data science and machine learning libraries, while JavaScript is better in front-end development libraries and frameworks. 3) Both have rich learning resources, but Python is suitable for starting with official documents, while JavaScript is better with MDNWebDocs. The choice should be based on project needs and personal interests.

The shift from C/C to JavaScript requires adapting to dynamic typing, garbage collection and asynchronous programming. 1) C/C is a statically typed language that requires manual memory management, while JavaScript is dynamically typed and garbage collection is automatically processed. 2) C/C needs to be compiled into machine code, while JavaScript is an interpreted language. 3) JavaScript introduces concepts such as closures, prototype chains and Promise, which enhances flexibility and asynchronous programming capabilities.

Different JavaScript engines have different effects when parsing and executing JavaScript code, because the implementation principles and optimization strategies of each engine differ. 1. Lexical analysis: convert source code into lexical unit. 2. Grammar analysis: Generate an abstract syntax tree. 3. Optimization and compilation: Generate machine code through the JIT compiler. 4. Execute: Run the machine code. V8 engine optimizes through instant compilation and hidden class, SpiderMonkey uses a type inference system, resulting in different performance performance on the same code.

JavaScript's applications in the real world include server-side programming, mobile application development and Internet of Things control: 1. Server-side programming is realized through Node.js, suitable for high concurrent request processing. 2. Mobile application development is carried out through ReactNative and supports cross-platform deployment. 3. Used for IoT device control through Johnny-Five library, suitable for hardware interaction.


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

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

Hot Article

Hot Tools

SublimeText3 Linux new version
SublimeText3 Linux latest version

Dreamweaver Mac version
Visual web development tools

ZendStudio 13.5.1 Mac
Powerful PHP integrated development environment

SecLists
SecLists is the ultimate security tester's companion. It is a collection of various types of lists that are frequently used during security assessments, all in one place. SecLists helps make security testing more efficient and productive by conveniently providing all the lists a security tester might need. List types include usernames, passwords, URLs, fuzzing payloads, sensitive data patterns, web shells, and more. The tester can simply pull this repository onto a new test machine and he will have access to every type of list he needs.

SublimeText3 Mac version
God-level code editing software (SublimeText3)