sajad torkamani

In a nutshell

TypeScript’s template literal types let you create string literal types using a syntax that’s similar to JavaScript’s template literal strings. You can use it for simple to advanced use cases.

Recipes

Use single literal type

type World = "world";
 
type Greeting = `hello ${World}`;
        
// The type of Greeting is now the stringl literal "hello world"

Try on TS playground

Combine multiple literal types

type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";
 
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;

// The type of AllLocaleIDs is now "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"

Try on TS playground

Do some advanced type juggling

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 => {
    //                        ^?
    console.log(`new name is ${newName.toUpperCase()}`);
});

person.on("ageChanged", newAge => {
    //                  ^?
    if (newAge < 0) {
        console.warn("warning! negative age");
    }
})

In the PropEventSource<Type> type, we use a template literal type and IndexedAccess to create a ${Key}Changed event name like `on.(firstNameChange') which will also receive the correct argument in the callback of type string.

Try on TS playground

Sources

Tagged: TypeScript

Leave a comment

Your email address will not be published. Required fields are marked *