TL;DR

Question:个 如何创建类型转换器,以获取使用具有索引签名的类型类型化的对象的已定义键?


我想在TypeScrip中创建一个类型"Converter",它获取一个类型A并返回一个新类型B,该类型将键作为A中定义的所有键,并且只接受字符串作为值,如下例所示:

type AToB<
   T,
   K extends keyof T = keyof T
> = { [id in K]: string };

const a = {
    x : {}
}

const b : AToB<typeof a> = {
    x : "here it works"
}

b works b works

但是,当我在已经定义了索引签名的类型的对象中使用它时,key of没有获得定义的键,例如:

type AWithSig = {
    [id : string]: {};
}

const aSig : AWithSig = {
    y: {},
    z: {}
}

const bSig : AToB<typeof aSig> = {
    y : "here the keys",
    z : "are not shown :("
}

我在TSPlayGround(link here)中try 了一下,但它不能识别ASIG中定义的键.

bSig doesn't work bSig doesn't work

bSig works if I define the keys manually bSig works if I define the keys manually

推荐答案

问题不在于AtoB,而在于aSig的类型.

当您使用(非union)类型为annotate a variable时,这是变量的类型.它不会从用来初始化变量的表达式中获得任何更具体的信息.

所以,当你写到const aSig: AWithSig = ⋯的时候,你已经失go 了你所关心的类型.

您最初注释的唯一原因大概就是check,即初始化表达式的类型是有效的AWithSig.如果是这样的话,您可以在该表达式上使用the satisfies operator来执行判断,然后只允许编译器在之后对aSig的类型执行infer:

const aSig = {
    y: {},
    z: undefined // error!
//  ~ <-- undefined is not assignable to {}
} satisfies AWithSig;

哎呀,我犯了一个错误,编译器发现了:

const aSig = {
    y: {},
    z: {}
} satisfies AWithSig // okay

现在aSig的类型是

/* const aSig: {
    y: {};
    z: {};
} */

这将与预期的AtoB:

const bSig: AToB<typeof aSig> = {
    y: "here it doesn't work :(",
    z: "abc"
}
// const bSig: AToB<{ y: {}; z: {}; }, "y" | "z"> 

Playground link to code

Typescript相关问答推荐

HttpClient中的文本响应类型选项使用什么编码?''

具有动态键的泛型类型

对于合并对象中的可选属性(例如选项),类型推断可能是错误的

如何在Angular 12中创建DisplayBlock组件?

如何从扩展接口推断泛型?

从对象类型描述生成类型

如何在Vue中使用Enum作为传递属性的键类型?

如何编写在返回函数上分配了属性的类型安全泛型闭包

FatalError:Error TS6046:';--模块分辨率';选项的参数必须是:'; node ';,'; node 16';,'; node

为什么S struct 类型化(即鸭子类型化)需要非严格类型联合?

在组件卸载时try 使用useEffect清除状态时发生冲突.react +打字

我可以使用typeof来强制执行某些字符串吗

使用来自API调用的JSON数据的Angular

为什么TypeScrip假定{...省略类型,缺少类型,...选取类型,添加&>}为省略类型,缺少类型?

完全在类型系统中构建的东西意味着什么?

T的typeof键的Typescript定义

Typescript泛型-键入对象时保持推理

Typescript 是否可以区分泛型参数 void?

带动态键的 TS 功能

为什么我无法在 TypeScript 中重新分配函数?