Description
TypeScript supports template literal strings1, with the same syntax as JavaScript Template Literals. There is also an in progress PR to switch to type alias helpers2.
Syntax
type World = "world";
type Greeting = `hello ${World}`;
// same as
// type Greeting = "hello world";
Examples
Unions
With unions you can mix and match.
type Color = "red" | "blue";
type Quantity = "one" | "two";
type SeussFish = `${Quantity | Color} fish`;
// same as
// type SeussFish = "one fish" | "two fish"
// | "red fish" | "blue fish";
type VerticalAlignment = "top" | "middle" | "bottom";
type HorizontalAlignment = "left" | "center" | "right";
// Takes
// | "top-left" | "top-center" | "top-right"
// | "middle-left" | "middle-center" | "middle-right"
// | "bottom-left" | "bottom-center" | "bottom-right"
declare function setAlignment(value: `${VerticalAlignment}-${HorizontalAlignment}`): void;
setAlignment("top-left"); // works!
setAlignment("top-middel"); // error!
setAlignment("top-pot"); // error! but good doughnuts if you're ever in Seattle
Objects
type PropEventSource<T> = {
on(eventName: `${string & keyof T}Changed`, callback: () => void): void;
};
/// Create a "watched object" with an 'on' method
/// so that you can watch for changes to properties.
declare function makeWatchedObject<T>(obj: T): T & PropEventSource<T>;
let person = makeWatchedObject({
firstName: "Homer",
age: 42, // give-or-take
location: "Springfield",
});
// error!
person.on("firstName", () => {
});
// error!
person.on("frstNameChanged", () => {
});
// success!
person.on("firstNameChanged", () => {
console.log(`firstName was changed!`);
});
Inference
Here we made on into a generic method. When a user calls with the string
'firstNameChanged'
, TypeScript will try to infer the right type for
K
. To do that, it will match K
against the content prior to
"Changed"
and infer the string "firstName"
. Once TypeScript figures
that out, the on
method can fetch the type of firstName
on the
original object, which is string
in this case. Similarly, when we call
with "ageChanged"
, it finds the type for the property age
which is
number
).
type PropEventSource<T> = {
on<K extends string & keyof T>
(eventName: `${K}Changed`, callback: (newValue: T[K]) => void ): void;
};
declare function makeWatchedObject<T>(obj: T): T & PropEventSource<T>;
let person = makeWatchedObject({
firstName: "Homer",
age: 42,
location: "Springfield",
});
// works! 'newName' is typed as 'string'
person.on("firstNameChanged", newName => {
// 'newName' has the type of 'firstName'
console.log(`new name is ${newName.toUpperCase()}`);
});
// works! 'newAge' is typed as 'number'
person.on("ageChanged", newAge => {
if (newAge < 0) {
console.log("warning! negative age");
}
})