我正在寻找一种方法,当一种类型的键包含另一种类型的键时,可以替换该类型的键. 每个密钥将仅包含一个单一替换(在示例中,"abc"只在密钥中存在一次),该替换需要映射到其在替换类型中的等效值.

目前我正在做:

type Replacements = {
    abc: 'cba';
    bcd: 'dcb';
};

type Test = {
    bcd: string;
    aabca: string;
};

type ReplaceKeys<R extends Record<string, string>, T extends Record<string, unknown>> = {
    [K in keyof T as K extends keyof R ? R[K] : K]: T[K];
  };
type Replaced = ReplaceKeys<Replacements, Test>;

这给了我预期的这种类型:

type Replaced = {
    dcb: string;
    aabca: string;
}

不过,我想知道是否有一种方法可以替换包含的密钥.我try 创建一个类型来完成这项工作:

type ReplaceKeysInferred<
    R extends Record<string, string>,
    T extends Record<string, unknown>,
> = {
    [K in keyof T as K extends `${infer A}${infer RKey extends keyof R & string}${infer B}`
        ? `${A}${R[RKey]}${B}`
        : K]: T[K];
};

type ReplacedInferred = ReplaceKeysInferred<Replacements, Test>;

然而,返回的类型不包含任何替换:

type ReplacedInferred = {
    bcd: string;
    aabca: string;
}

不确定我哪里出了问题.

编辑: 我还try 过:

type ReplaceKeysInferred<
    R extends Record<string, string>,
    T extends Record<string, unknown>,
> = {
    [K in keyof T as K extends `${infer A}${infer RKey}${infer B}`
        ? RKey extends keyof R
            ? `${A}${R[RKey]}${B}`
            : K
        : K]: T[K];
};

这本质上是对第一种类型的额外步骤.这与第一种回馈类型的运作方式相同:

type ReplacedInferred = {
    bcd: string;
    aabca: string;
}

它仍然没有替换任何包含的字符串.

推荐答案

一般的方法是

type ReplaceKeysInferred<R extends { [k: string]: string }, T extends object> = {
    [K in keyof T as K extends string ? Substitute<R, K> : K]: T[K]
}

其中Substitute<R, K>替换字符串K内来自R的任何匹配条目. 实现Substitute的一种方法是:

type Substitute<R extends { [k: string]: string }, T extends string> =
    OrDefault<{ [K in string & keyof R]:
        T extends `${infer F}${K}${infer L}` ? `${F}${R[K]}${L}` : never
    }[string & keyof R], T>

type OrDefault<T, U> = [T] extends [never] ? U : T;
   

我们对R的字符串键进行mapping over,并try 将每个这样的键KT的某个子字符串进行匹配. 这使用形式T extends `${infer F}${K}${infer L}` ? `${F}${R[K]}${L}` : nevertemplate literal推断.因此,如果K出现在T中,则会被R[K]替换.否则我们得到never.

K extends `${infer A}${infer RKey}${infer B}`有效而K extends `${infer A}${infer RKey}${infer B}`无效的原因是因为ARKeyB都是placeholder generic类型参数.根据microsoft/TypeScript#40336中的描述,实现模板字面量的PR类型,"通过从源推断单个字符来匹配紧随其后的占位符."这意味着ARKey最多只有一个字符长. 而在`${infer F}${K}${infer L}`中,K不是占位符.

无论如何,然后我们用string & keyof R将映射类型index into,导致R中的每个键K的替换为union. 我们预计最多会有一个这样的替代,因此unions 要么有一个成员,要么是never个.

OrDefault<T, U>实用类型负责never. 如果替换存在,那么我们返回它.否则,我们返回原始密钥K. 这样,失败的替换不会从输出中删除键.


好吧,让我们测试一下:

type Replacements = {
    abc: 'cba';
    bcd: 'dcb';
};

type Test = {
    bcd: string;
    aabca: string;
};

type ReplacedInferred = ReplaceKeysInferred<Replacements, Test>;
/* type ReplacedInferred = {
    dcb: string;
    acbaa: string;
} */

看起来不错请注意,上述Substitute的实现很大程度上取决于问题中所述的用例.future 有不同要求(例如多个或重叠匹配)的读者将需要相应地改变方法.

Playground link to code

Typescript相关问答推荐

如何使用TypScript限制允许对JavaScript值进行的操作集?

接口DOMRouter选项未输出

无需刷新即可让现场访客逆变

创建一个通用上下文,允许正确推断其中的子项

如何在ts中使用函数重载推断参数类型

我用相同的Redux—Toolkit查询同时呈现两个不同的组件,但使用不同的参数

为什么TypeScript假设文档对象始终可用?

使用泛型keyof索引类型以在if-condition内类型推断

无法正确推断嵌套泛型?

在嵌套属性中使用Required

路由链接始终返回到

Angular文件上传到Spring Boot失败,多部分边界拒绝

TypeScrip:强制使用动态密钥

根据类型脚本中的泛型属性 Select 类型

如何获取受类型脚本泛型约束的有效输入参数

打字错误推断

与toast.error一起react 中的Async TUNK错误消息

对有效枚举值的常量字符串进行常规判断

打印脚本中正则函数和函数表达式的不同类型缩小行为

换行TypeScript';s具有泛型类型的推断数据类型