我想让Typescript根据对象的形状推断对象的类型。 假设我们具有以下类型定义:
type Base = {
id: string;
attributes: any;
}
type ShapeOne = Base & {
id: "one";
attributes: {
amount: 1 | 2 | 3;
}
}
type ShapeTwo = Base & {
id: "two";
attributes: {
size: "S" | "M" | "L";
}
}
还有一个与ShapeOne
相匹配的对象:
const shape = {
id: "one",
attributes: {
amount: 1,
}
}
现在我希望Typescript可以根据对象的形状提取/推断正确的类型:
type t = ShapeType<typeof shape>; // ShapeOne
通过阅读文档,嵌套条件类型应该可以实现这一点,但是不幸的是,我不知道对此有何进一步的了解。
答案 0 :(得分:1)
按照惯例,您应该对此类对象结构使用interface
而不是type
别名:
interface Base {
id: string;
attributes: any;
}
interface ShapeOne extends Base {
id: "one";
attributes: {
amount: 1 | 2 | 3;
}
}
interface ShapeTwo extends Base {
id: "two";
attributes: {
size: "S" | "M" | "L";
}
}
这样做不是必需的,但是,如果不这样做,最终结果可能会显示为ShapeOne
的定义,而不会在任何地方显示名称“ ShapeOne
” 。同样,由于(例如){id: "two"; attributes: { size: "S" | "M" | "L"; }}
已经是Base
的子类型,因此没有真正的理由使用交集。只会使事情变得混乱。
无论如何,您都会想出一种实际的discriminated union类型,因为它在这里很有用:
type Shape = ShapeOne | ShapeTwo;
此后,我们需要更改定义shape
的方式。在您的情况下,编译器会将其类型推断为
/* const shape: {
id: string;
attributes: {
amount: number;
};
} */
碰巧,这样的类型将被视为可分配给ShapeOne
,但是有些偶然。编译器忘记了shape.id
是literal string type "one"
。它一直扩展到string
。如果您有一个看起来像ShapeThree
的{{1}},则编译器会认为{id: "three"; attributes: {amount: 4 | 5 | 6;}}
可分配给shape
或ShapeOne
。
因此,您需要缩小ShapeThree
的范围。您可以根据需要将其注释为shape
:
Shape
编译器会自动将const shape: Shape = {
id: "one",
attributes: {
amount: 1,
}
}
的外观类型缩小control flow analysis到shape
:
ShapeOne
与type t0 = typeof shape; // ShapeOne
完全不需要。
或者,如果您不想将ShapeType<>
注释为联合,则仍需要使用更窄的类型,可能通过const
assertion:
shape
请注意,const shapeNarrow = {
id: "one",
attributes: {
amount: 1,
}
} as const;
/* const shapeNarrow: {
readonly id: "one";
readonly attributes: {
readonly amount: 1;
};
} */
的类型现在是shapeNarrow
的子类型:更具体。然后,您终于可以编写嵌套的distributive conditional type来拔出工会的合适成员:
ShapeOne
所以您可以这样写type ExtractSubtype<T, U> = T extends any ? U extends T ? T : never : never;
type t = ExtractSubtype<Shape, typeof shapeNarrow>;
// type t = ShapeOne
:
ShapeType<>
是的!
请注意,分布式条件类型仅适用于裸类型参数,因此您不能重构为:
type ShapeType<U> = ExtractSubtype<Shape, U>;
type tGood = ShapeType<typeof shapeNarrow>; // ShapeOne ☺