[Typescript] Dynamic types: Use TypeScript's Mapped Types and Template Literal Types Together

we're going to dive deep into a more complex example in which we combine mapped types, key remapping, template literal types, and indexed access types to statically type a highly dynamic JavaScript function in TypeScript.

Start with following code:

复制代码
function createGetterObject(obj: any): any {
    const newObj: any = {};
    for (const key of Object.keys(obj)) {
        const cpK = key[0].toUpperCase() + key.substr(1);
        const getterKey = `get${cpK}`;
        newObj[getterKey] = () => obj[key];
    }
    return newObj;
}

const user = createGetterObject({
    name: "Wan",
    twitter: "zhentiw"
})

console.log(user)
console.log(user.getName())
console.log(user.getTwitter())
复制代码

 

We want to get better typings support.

复制代码
function createGetterObject<T>(obj: T): PropGetters<T> {
    const newObj: any = {};
    for (const key of Object.keys(obj)) {
        const cpK = key[0].toUpperCase() + key.substr(1);
        const getterKey = `get${cpK}`;
        newObj[getterKey] = () => obj[key];
    }
    return newObj;
}

type PropGetters<T> = {

}
复制代码

Get an error:

This is because `T` can be any as well. We need to limit T type by telling that `T` can be only for Object:

function createGetterObject<T extends Record<string, any>>(obj: T): PropGetters<T> {

 

In `PropGetter`, we want to create similar to 

type PropGetters<T> = {
  getName: () => sting
  getTwitter: () => string
}

How to make those?

 

We can start from:

type PropGetters<T> = {
 [Key in keyof T]: () => T[Key]
}

keyof T: get all the keys of T, so we got `name` & `twitter`

T[Key]: as lookup type, `name` prop result in string `Wan`.

 

This is what we get as a result:

`name` & `twitter` are functions type which return string, not really what we want.

 

Template Literal Types

type PropGetters<T> = {
 [Key in keyof T as `get${Key}`]: () => T[Key]
}

Got error:

This is because Key can be string, number, boolean... , what we want is just string type, so we can do:

type PropGetters<T> = {
 [Key in string & keyof T as `get${Key}`]: () => T[Key]
}

Now, we got:

We actual want `getName` not as `getname`, to fix this, we can do:

type PropGetters<T> = {
 [Key in string & keyof T as `get${Capitalize<Key>}`]: () => T[Key]
}

 

Now the type should works. If we add another prop `id`:

Typescript can tell the return value is a number type.

 

Final piece to limit `T` in `PropGetters`:

type PropGetters<T extends Record<string, any>> = {
 [Key in string & keyof T as `get${Capitalize<Key>}`]: () => T[Key]
}

 

 

Read: https://egghead.io/lessons/typescript-use-typescript-s-mapped-types-and-template-literal-types-together

posted @   Zhentiw  阅读(99)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2020-05-14 [Mise] Focus in input field on page load with `x-init` in Alpine JS
2020-05-14 [Mise] Focus an input field on button click with `x-ref` and the `$refs` property in Alpine JS
2020-05-14 [Mise] Keep a DOM input and state value in sync with the `x-model` directive in Alpine JS
2020-05-14 [Mise] Control enter and leave transitions with the `x-show.transition` modifier in Alpine JS
2020-05-14 [Mise] Iterate through data with the `x-for` attribute in Alpine JS
2020-05-14 [Mise] Toggle visibility and styles based on state with `x-show` and `x-bind` in Alpine JS
2020-05-14 [Mise] Update a count state value with the x-on event listener directive in Alpine JS
点击右上角即可分享
微信分享提示