【问题标题】:Declare TypeScript type while inferring some part of that type在推断该类型的某些部分时声明 TypeScript 类型
【发布时间】:2021-04-25 15:27:24
【问题描述】:

假设您有多个模块,并且您希望每个模块都导出某种特定形状的对象。同时,对象的属性可能具有您想要在导入模块时推断的变量类型。有没有办法声明对象的类型(这样你在编写对象时可以自动完成),而且还能够推断出特定对象的属性类型?

这是一个显然不起作用的示例,因为我们明确设置要推断的类型:

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 }

我了解,如果我避免明确提供 ab 的类型,则会推断出这些类型。但是在编写模块时我失去了自动完成功能(即我无法开始输入 m 并看到 myProp 是一个有效的属性)。

同样,我不想要求用户明确地向MyObject 提供类型。在这个例子中,它只是一个numberstring,但实际上这种类型可能非常复杂,我不希望用户在他们已经写出确定输入。

此外,我知道我可以使用单个模块和一个函数来做到这一点,例如:

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> {
  //
}

但问题是如何跨模块边界实现相同的结果?

【问题讨论】:

标签: typescript


【解决方案1】:

注意:请参阅分隔线下方的更新。

ab 的整个类型都可以推断出来,根本不需要对它们进行类型声明。

// 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;
    };
}

其中的ab 属性与MyObject&lt;T&gt; 兼容,因为TypeScript 的类型系统是结构化的,而不是名义上的。类型的名称是什么并不重要,重要的是它定义的形状。

通过以上内容,您可以获得自动完成信息(至少在 VSCode 和 TypeScript 游乐场中):

或者,如果您愿意,可以通过提供属性的类型而不是 any 来将类型放在它们上面:

export const a: MyObject<number> = { myProp: 42 };

在评论中,您已澄清不是您使用 objectMap 的地方需要自动完成功能,而是您定义 ab 的地方。很抱歉造成误解。

这与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&lt;&gt; = { 具有相同效果的东西,但据我所知没有...)

【讨论】:

  • 对不起,如果问题不清楚。我知道我可以省略 ab 的类型并让 TS 推断这些类型。但是,如果我这样做,我现在在编写模块时会失去自动完成功能(即我不知道 myProp 是一个有效的属性)。同样,我不想将显式类型变量传递给MyObject,因为重点是推断这种类型——在这个例子中,它是一个简单的标量类型,但实际上这可能是一个非常复杂的类型,我不知道'不希望图书馆用户不得不手写。
  • @DanielRearden - 正如我在回答中所说,自动完成在这种情况下应该可以正常工作,并且在 VSCode 和 TypeScript 游乐场中也可以。你在不使用的地方使用什么?也许这是一个工具问题?
  • 感谢您的回答 T.J.就像我试图在评论中扩展并对问题进行编辑一样,这里的问题不是index.ts 内部的自动完成,而是内部的自动完成,例如moduleA。换句话说,我想输入export const a = { m 并查看myProp 作为建议。不过,我知道的唯一方法是首先明确提供 a 的类型。
  • 据我了解,a 的类型既可以显式定义,也可以从文字值中推断出来。如果是推断出来的,则没有自动完成功能可用。
  • 啊!好的,对不起。我已经更新了答案。恐怕如果没有函数,你就无法获得这种推断,但无操作函数并不昂贵。
猜你喜欢
  • 1970-01-01
  • 2019-06-29
  • 1970-01-01
  • 2019-09-17
  • 2018-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-13
相关资源
最近更新 更多