假设您有多个模块,并且您希望每个模块都导出某个特定形状的对象。同时,对象的属性可能具有您想要在导入模块时推断的变量类型。有没有办法声明对象的类型(这样你在编写对象时就可以自动补全),同时还能推断出特定对象的属性类型?
这是一个显然不起作用的示例,因为我们明确设置了要推断的类型:
type MyObject<T> = { myProp: T }
// moduleA.ts
export const a: MyObject<any> = ({ myProp: 42 });
// moduleB.ts
export const b: MyObject<any> = ({ myProp: "A string" });
// index.ts
import { a } from "./moduleA";
import { b } from "./moduleB";
const objectMap = { a, b };
type ObjectMap = typeof objectMap
type InferredMap = {
[P in keyof ObjectMap]: ObjectMap[P] extends { myProp: infer R } ? R : unknown
}
结果类型不出所料是 { a: any, b: any }
。
我们如何在编写模块时保持自动完成功能的同时制作此 { a: number, b: string }
?
我明白,如果我避免明确提供 a
和 b
的类型,这些将被推断出来。但是在编写模块时我失去了自动完成功能(即我无法开始输入 m
并看到 myProp
是一个有效的属性)。
同样,我不想要求用户明确地向 MyObject
提供类型。在这个例子中,它只是一个 number
或 string
,但实际上这种类型可能非常复杂,我不希望用户在他们已经写出实际决定类型的值。
此外,我知道我可以用一个模块和一个函数来做到这一点,例如:
type MappedType<
T extends { [key: string]: { myProp: any } }
> = {
[P in keyof T]: T[P] extends { myProp: infer R } ? R : unknown
}
function getResult<T extends { [key: string]: { myProp: any } }> (map: T): MappedType<T> {
//
}
但问题是如何跨模块边界实现相同的结果?
答案 0 :(得分:2)
注意:请参阅分隔线下方的更新。
可以推断 a
和 b
的整个类型,您根本不需要对它们进行类型声明。
// moduleA.ts
export const a = { myProp: 42 };
// moduleB.ts
export const b = { myProp: "A string" };
// index.ts
import { a } from "./moduleA";
import { b } from "./moduleB";
const objectMap = { a, b };
objectMap
的类型将是
{
a: {
myProp: number;
};
b: {
myProp: string;
};
}
a
和 b
属性与 MyObject<T>
兼容,因为 TypeScript 的类型系统是结构性的,而不是名义上的。类型的名称是什么并不重要,重要的是它定义的形状。
通过上述内容,您可以获得自动完成信息(至少在 VSCode 和 TypeScript 游乐场中):
或者,如果您愿意,您可以通过提供属性的类型而不是 any
来将类型放在它们上:
export const a: MyObject<number> = { myProp: 42 };
在评论中,您已经澄清,不是在您希望自动完成的地方使用 objectMap
,而是在定义 a
和 b
的地方。抱歉误会了。
这与 my question here 非常相似。据我所知,你只能用一个函数来做到这一点:
function makeMyObject<T>(obj: MyObject<T>): MyObject<T> {
return obj;
}
然后当你输入:
export const a = makeMyObject({m│
// ^−−−− (cursor)
...它提供 myProp: unknown
作为完成选项:
有时您必须求助于无所作为的功能才能获得所需的推理。 :-| (我想看到一个具有相同效果的 Java 风格的 export const a: MyObject<> = {
东西,但据我所知没有...)