打字稿,更改嵌套在界面中的所有匹配键的类型

Posted

技术标签:

【中文标题】打字稿,更改嵌套在界面中的所有匹配键的类型【英文标题】:Typescript, change types of all matching keys nested in interface 【发布时间】:2021-07-17 07:45:50 【问题描述】:

是否可以更改嵌套接口中所有匹配键的类型?

我尝试了几种不同的方法,但都没有成功。我试图保持我的类型的通用签名,如我的示例中所述。我想指定要更改的键/键(“Target”)以及应该是什么类型(“NewType”)。我只想识别键来改变它们的类型。

请参考下面我的界面及其预期结果。我转换界面的方法如下。

似乎第二种方法应该可行,但我不知道如何在递归结束后有条件地检查接口的叶键值(参见第二个示例中的条件)。

Here's a sandbox link as well.

任何帮助将不胜感激。谢谢!

// interface 
interface og 
    a: number,
    b: number,
    c: 
        a: number,
        b: number,
        characters: 
            woman: string,
            man: string,
            elf: [boolean, string, number]
            more: 
                elf: boolean
            
        ,
    ,
    elf: boolean,
;

// desired transform result (I know "dobby" isn't a typical type)
type og_transformed = 
    a: number,
    b: number,
    c: 
        a: number,
        b: number,
        characters: 
            woman: string,
            man: string,
            elf: ["dobby", "dobby", "dobby"] 
            more: 
                elf: "dobby"
            
        ,
    ,
    elf: "dobby",
;

// this result could be accepted as well
type og_transformed = 
    a: number,
    b: number,
    c: 
        a: number,
        b: number,
        characters: 
            woman: string,
            man: string,
            elf: "dobby" // <- this is different
            more: 
                elf: "dobby"
            
        ,
    ,
    elf: "dobby",
;

方法 1:

// transforming type
type ChangeTypeOfKeys_1<Obj, Target, NewType> = 
        [K in keyof Obj]: K extends Target
            ? NewType
            : Obj[K]
    

// type call
type dobby_1 = ChangeTypeOfKeys_1<og, 'elf', 'dobby'>

// result
type dobby_1 = 
    a: number;
    b: number;
    c: 
        a: number;
        b: number;
        characters: 
            woman: string;
            man: string;
            elf: [boolean, string, number];
            more: 
                elf: boolean;
            ;
        ;
    ;
    elf: "dobby";

方法 2:

// transforming type
type ChangeTypeOfKeys_2<Obj, Target, NewType> = Obj extends object 
    ?  
        [K in keyof Obj]: ChangeTypeOfKeys_2<Obj[K], Target, NewType>
    
    : NewType // <- how do I check the key's value in this conditional ???

// type call
type dobby_2 = ChangeTypeOfKeys_2<og, 'elf', 'dobby'>

// result
type dobby_2 = 
    a: "dobby";
    b: "dobby";
    c: 
        a: "dobby";
        b: "dobby";
        characters: 
            woman: "dobby";
            man: "dobby";
            elf: ["dobby", "dobby", "dobby"];
            more: 
                elf: "dobby";
            ;
        ;
    ;
    elf: "dobby";

解决方案(来自下面的 Alex)

这里主要是为了在下面的 cmets 中显示 on hover 参考,并避免任何人忽视如此简单的事情,例如我自己...

我还添加了一个解决方案沙箱,其中包含扩展类型和每个功能的描述。为了清晰起见,示例界面已被修改。

Solution sandbox

// transforming type
type ChangeTypeOfKeys_3<Obj, Target, NewType> = 
    [K in keyof Obj]: K extends Target 
        ? NewType
        : Obj[K] extends object 
            ? ChangeTypeOfKeys_3<Obj[K], Target, NewType>
            : Obj[K]


// type call
type dobby_3 = ChangeTypeOfKeys_3<og, 'elf', 'dobby'>

// result (on hover)
type dobby_3 = 
    a: number;
    b: number;
    c: ChangeTypeOfKeys_3<
        a: number;
        b: number;
        characters: 
            woman: string;
            man: string;
            elf: [boolean, string, number];
            more: 
                elf: boolean;
            ;
        ;
    , "elf", "dobby">;
    elf: "dobby";

【问题讨论】:

【参考方案1】:

您肯定需要它是递归的以深入嵌套对象。但实际上你需要做两个条件测试:

    这是应该更换的钥匙吗?您已经在这样做了。 这个值是一个需要递归的对象吗?如果是,则递归,否则我们只返回其类型不变。

你的两次尝试各做一半。您需要将它们组合成一个嵌套的条件类型来执行这两项检查。

type ChangeTypeOfKeys<Obj, Target, NewType> = 
    [K in keyof Obj]:

        // does this value have the target key? If so replace it.
        K extends Target ? NewType 

        // Else, is this value an object? If so, recursively change call ChangeTypeOfKeys
        : Obj[K] extends object ? ChangeTypeOfKeys<Obj[K], Target, NewType>

        // Else, use the type of this leaf without modification.
        : Obj[K]


type OgTransformed = ChangeTypeOfKeys<Og, 'elf', 'dobby'>
declare const elfed: OgTransformed
const elfName = elfed.c.characters.elf // type: 'dobby'
const elfMore = elfed.c.characters.more.elf // type: 'dobby'

Playground

【讨论】:

啊。非常感谢您的简洁答复。效果很好。 从您的回答中,我意识到我实际上在其他迭代中正确设置了变压器类型。不幸的是,我没有意识到 on hover TS 不会一直显示类型。它只是显示变压器类型的下一个递归。我不知道您实际上必须像在答案末尾那样以声明方式访问类型。直到。 我已将我所说的on hover 包含在我的原始帖子中,供任何可能阅读此内容的人使用。 是的,类型脚本并不总是完全处理工具提示中的类型。请注意内部类型是如何包装在ChangeTypeOfKeys&lt; ... &gt; 中的,这表明正在转换的输入类型和进行转换的类型别名。从它的嵌套值中获取真实类型需要深入研究。

以上是关于打字稿,更改嵌套在界面中的所有匹配键的类型的主要内容,如果未能解决你的问题,请参考以下文章

具有未知键的对象的打字稿类型,但只有数值?

无法让嵌套类型保护与打字稿中的联合类型一起使用

打字稿:嵌套对象的深度键

打字稿中具有联合类型键的松散类型对象

打字稿:如何制作接受对象的类型,其键匹配通用但所有值都是值参数的映射函数

打字稿和嵌套解构