注释类型时打字稿映射类型错误
Typescript mapped type error when type is annotated
不确定标题是否明确(很难用一句话描述这个问题)但这里有一些代码可以说明这个问题(代码块后有更多关于该问题的详细信息):
type AttributeDefinition = {
name:string
type: 'string' | 'number',
}
type EntityDefinition = {
attributes: Readonly<AttributeDefinition[]>
}
export type Entity<ET extends EntityDefinition> = {
[K in ET['attributes'][number] as K['name']]:
K['type'] extends 'string' ? string :
K['type'] extends 'number' ? number :
never
}
const def1 = {
attributes: [
{name:'foo', type: 'string'},
{name:'baz', type: 'number'},
] as const
}
const def2: EntityDefinition = {
attributes: [
{name:'foo', type: 'string'},
{name:'baz', type: 'number'},
] as const
}
const entity1: Entity<typeof def1> = {
foo: 'bar',
baz: 42
}
const entity2: Entity<typeof def2> = {
foo: 'bar',
baz: 42
}
所以我的问题是,当我想将 'bar'
分配给 foo
(以及 42
分配给 baz
时,打字稿会在 entity2
上抛出错误.问题是 TS2322: Type 'string' is not assignable to type 'never'
.
但是,entity1
不会出现此问题。
这个问题的根源似乎与我注释EntityDefinition
类型的def2
的声明有关。我这样做是为了让 IDE 的输入提示帮助我正确声明 def2
object.
但是 def1
object,即使没有类型注释,稍后在创建 entity1
时也会得到正确处理,而 def2
(我希望是以正确的方式输入)在用于创建 entity2
.
时失败
我不太确定为什么它会以这种方式失败,我如何才能像注释 def2
一样注释 object 并使用这样的 object 后来就像我为 entity1
.
所做的那样
谢谢!
我相信即将推出的功能可以解决此特定用例:"satisfies" operator to ensure an expression matches some type (feedback reset) #47920
const def2 = {
attributes: [
{name:'foo', type: 'string'},
{name:'baz', type: 'number'},
] as const
} satisfies EntityDefinition;
它仍在开发中,但您可以在 Staging Playground
查看工作示例
在 进入 TS 之前,您可以使用受约束的标识函数来提供 IntelliSense 帮助,同时在编辑器中物理键入您的定义,同时仍然允许编译器推断其(更具体) 文字属性:
function createEntityDefinition <T extends EntityDefinition>(def: T): T {
return def;
}
const def2 = createEntityDefinition({
attributes: [
{name:'foo', type: 'string'},
{name:'baz', type: 'number'},
] as const,
});
const entity2: Entity<typeof def2> = {
foo: 'bar',
baz: 42,
}; // ok
Entity<typeof def2>
失败的原因是注释加宽了 def2
的类型,这导致细节丢失。 Entity<typeof def2>
的类型等于 Entity<EntityDefinition>
,计算结果为 {[x: string]: never}
。
您或许可以创建一个保留信息的泛型类型注释,但是您必须在常量类型中指定泛型参数,这会导致代码重复。
处理这个问题的方法是创建一个构造函数,它只是 returns 参数但对其有约束:
const mkEntityDefinition = <T extends EntityDefinition>(def: T) => def
你可以这样使用它:
const def3 = mkEntityDefinition({
attributes: [
{name:'foo', type: 'string'},
{name:'baz', type: 'number'},
] as const
})
export const entity3: Entity<typeof def3> = {
foo: 'bar',
baz: 42
}
它会防止错误:
const defBad = mkEntityDefinition({
attributes: [
{name:'foo', type: 'strrring'},
{name:'baz', type: 'number'},
] as const
})
// Error: Type '"strrring"' is not assignable to type '"string" | "number"'.
// Did you mean '"string"'?
不确定标题是否明确(很难用一句话描述这个问题)但这里有一些代码可以说明这个问题(代码块后有更多关于该问题的详细信息):
type AttributeDefinition = {
name:string
type: 'string' | 'number',
}
type EntityDefinition = {
attributes: Readonly<AttributeDefinition[]>
}
export type Entity<ET extends EntityDefinition> = {
[K in ET['attributes'][number] as K['name']]:
K['type'] extends 'string' ? string :
K['type'] extends 'number' ? number :
never
}
const def1 = {
attributes: [
{name:'foo', type: 'string'},
{name:'baz', type: 'number'},
] as const
}
const def2: EntityDefinition = {
attributes: [
{name:'foo', type: 'string'},
{name:'baz', type: 'number'},
] as const
}
const entity1: Entity<typeof def1> = {
foo: 'bar',
baz: 42
}
const entity2: Entity<typeof def2> = {
foo: 'bar',
baz: 42
}
所以我的问题是,当我想将 'bar'
分配给 foo
(以及 42
分配给 baz
时,打字稿会在 entity2
上抛出错误.问题是 TS2322: Type 'string' is not assignable to type 'never'
.
但是,entity1
不会出现此问题。
这个问题的根源似乎与我注释EntityDefinition
类型的def2
的声明有关。我这样做是为了让 IDE 的输入提示帮助我正确声明 def2
object.
但是 def1
object,即使没有类型注释,稍后在创建 entity1
时也会得到正确处理,而 def2
(我希望是以正确的方式输入)在用于创建 entity2
.
我不太确定为什么它会以这种方式失败,我如何才能像注释 def2
一样注释 object 并使用这样的 object 后来就像我为 entity1
.
谢谢!
我相信即将推出的功能可以解决此特定用例:"satisfies" operator to ensure an expression matches some type (feedback reset) #47920
const def2 = {
attributes: [
{name:'foo', type: 'string'},
{name:'baz', type: 'number'},
] as const
} satisfies EntityDefinition;
它仍在开发中,但您可以在 Staging Playground
查看工作示例在
function createEntityDefinition <T extends EntityDefinition>(def: T): T {
return def;
}
const def2 = createEntityDefinition({
attributes: [
{name:'foo', type: 'string'},
{name:'baz', type: 'number'},
] as const,
});
const entity2: Entity<typeof def2> = {
foo: 'bar',
baz: 42,
}; // ok
Entity<typeof def2>
失败的原因是注释加宽了 def2
的类型,这导致细节丢失。 Entity<typeof def2>
的类型等于 Entity<EntityDefinition>
,计算结果为 {[x: string]: never}
。
您或许可以创建一个保留信息的泛型类型注释,但是您必须在常量类型中指定泛型参数,这会导致代码重复。
处理这个问题的方法是创建一个构造函数,它只是 returns 参数但对其有约束:
const mkEntityDefinition = <T extends EntityDefinition>(def: T) => def
你可以这样使用它:
const def3 = mkEntityDefinition({
attributes: [
{name:'foo', type: 'string'},
{name:'baz', type: 'number'},
] as const
})
export const entity3: Entity<typeof def3> = {
foo: 'bar',
baz: 42
}
它会防止错误:
const defBad = mkEntityDefinition({
attributes: [
{name:'foo', type: 'strrring'},
{name:'baz', type: 'number'},
] as const
})
// Error: Type '"strrring"' is not assignable to type '"string" | "number"'.
// Did you mean '"string"'?