繁体   English   中英

如何在 TypeScript 中推断 object function 参数的确切类型?

[英]How do I infer the exact type of an object function parameter in TypeScript?

我有一个 object ,其属性使用通用模板文字键入,该模板文字需要某个提供的(作为通用类型参数)substring 出现在字符串中。 目前,如果提供了无效值,则错误是无法将值分配给模板文字。

我想使错误消息包含带有自定义消息的类型。 例如Type 'never' is not assignable to type 'ErrorMessage<"You cannot do that.">'. 我见过这样做的库,但我不知道在这种情况下如何做。 我有一个创建这个的泛型,但是当它试图让它与对象一起工作时,属性不够窄。

我已经完成了以下工作:

interface ErrorMessage<T> {
  __msg: T;
}

type Substring<
  str extends string,
  substr extends string
> = str extends `${string}${substr}${string}`
  ? str
  : ErrorMessage<`No match: '${str}' does not contain '${substr}'`>;

type Substrings = {
  bar: "aaa";
};

type Foo<T extends { [K in keyof Substrings]: string }> = {
  [K in keyof T & keyof Substrings]: Substring<T[K], Substrings[K]>;
};

type Infer<T extends { [K in keyof Substrings]: string }> = {
  [K in keyof T]: T[K];
};

const helper =
  <T extends { [K in keyof Substrings]: string }>(val: Infer<T>) =>
  (val2: Foo<T>) =>
    val2;

const foo = helper({
  bar: "has bar aaa",
})({
  bar: "has bar aaa",
});

这在理论上可行,但重复的 function 调用很尴尬。 有什么办法可以让helper “自给自足”? 我尝试过的所有其他变体最终都会将 object 的属性类型推断为string 主要问题是 TypeScript 在使用其他工作的泛型时无法推断Thelper

我可以传递 type 参数,但这需要将 object 单独定义as const ,然后使用typeof ,这也很尴尬。

作为一种稍微不同的方法,以下方法几乎可以工作,除了K不是Foo映射中Substrings的有效索引:

type Foo<T extends { [K in keyof T & keyof Substrings]: string }> = {
  [K in keyof T]: Substring<T[K], Substrings[K]>;
};

const helper = <T extends { [K in keyof Substrings]: string }>(val: Foo<T>) =>
  val;

我可能已经用这种特殊的方法走上了死胡同。 如果你知道或能想出不同的方法,我很高兴。 我对我目前所拥有的东西没有特别的亲和力,这只是我迄今为止最终得到的东西。

我感觉会有一些 function 过载和currying,但我不够熟练来实现它。

提前致谢。

更新:带有常规错误消息的简单示例:

type Substring<substr extends string> = `${string}${substr}${string}`;

type Substrings = {
  bar: "aaa";
};

type Foo<T extends { [key: string]: string }> = {
  [K in keyof T]: Substring<T[K]>;
};

const helper = <T extends Foo<Substrings>>(val: T) => val;

const foo = helper({
  bar: "has bar aaa",
});

这是一种可能的方法:

const helper = <T extends Record<keyof Substrings, string>>(
    t: { [K in keyof T]: K extends keyof Substrings ? (
        T[K] extends `${string}${Substrings[K]}${string}` ?
        T[K] : ErrorMessage<`No match: '${T[K]}' does not contain '${Substrings[K]}'`>
    ) : T[K] }
) => t;

类型检查器在推断泛型类型参数时使用的算法有点繁琐。 编译器非常擅长从同态映射类型推断,这意味着它通常可以从类型{[K in keyof T]: ...T[K]...} T 但是T[K]出现在...T[K]...中的特定位置与推理的成功程度有很大关系。

我使用的一个经验法则是,如果您希望编译器从条件类型推断泛型X ,您应该确保X直接显示为检查类型,例如X extends... ? ... : ... X extends... ? ... : ... 由于我们希望编译器推断T[K] ,因此我们希望T[K] extends... 因此T[K] extends `${string}${Substrings[K]}${string}`? ... T[K] extends `${string}${Substrings[K]}${string}`? ...类型。 可能可以将其重构为它自己的命名类型别名,但这可能会产生奇怪的附加效果,我不想在这里讨论。


无论如何,让我们测试一下:

const foo1 = helper({
    bar: "has bar aaa",
}); // okay

const foo2 = helper({
    bar: "oopsie doodle" // error!
    //~ <-- 'ErrorMessage<"No match: 'oopsie doodle' does not contain 'aaa'">'
})

看起来不错。 您会收到您正在寻找的错误消息。

Playground 代码链接

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM