从值切换到函数时打字稿类型检查失败

时间:2021-01-06 10:00:23

标签: typescript typescript-generics

我正在编写一个函数,该函数将从对象中的指定键返回固定类型。因此该函数总是返回类型 string,但对于通用对象,您必须指定一个包含字符串的键:

type KeyFor<T, O, K extends keyof O> = O[K] extends T ? K : never;

function selectFromValue<O, K extends keyof O>(obj: O, path: KeyFor<string, O, K>) {
}

这似乎适用于以下示例:

interface Example {
  x: string,
  y: number,
};

const example: Example = {
  x: '',
  y: 7,
};

selectFromValue(example, 'x'); // Compiles, as expected.
selectFromValue(example, 'y'); // Errors, as expected.

然而,我想要第一个参数作为查找函数,而不是一个简单的值。其他类型相同:

function selectFromFunction<O, K extends keyof O>(get: () => O, path: KeyFor<string, O, K>) {
}

selectFromValue(() => example, 'x'); // Still working, because why wouldn't it?

但是当我向查找函数添加一个参数的那一刻,即使什么都不改变,它也会中断:

function selectWithParameter<O, K extends keyof O>(get: (state) => O, path: KeyFor<string, O, K>) {
}

selectWithParameter((arg) => example, 'x');

我尝试了带有额外泛型类型参数的 (state: any)(state: unknown)(state: A),它们都给出:

Argument of type 'string' is not assignable to parameter of type 'never'.

'x'。它工作的唯一方法是,如果 (arg) 本身被赋予一个显式类型,但我不明白为什么这会对类型检查 P[K] extends string 产生影响,因为它是一个完全不相关的类型:(arg: number) => example .

所以,在写这个问题时,我发现了以下问题:

Typescript generics: given key K and object T, constrain the type of T[K]

这让我可以将代码重写为:

function selectFromValue<O extends Record<K, string>, K extends string>(obj: O, path: K) {
}

function selectFromFunction<O extends Record<K, string>, K extends string>(obj: () => O, path: K) {
}

function selectWithParameter<O extends Record<K, string>, K extends string>(get: (state) => O, path: K) {
}

interface Example {
  x: string,
  y: number,
};

const example: Example = {
  x: '',
  y: 7,
};

selectFromValue(example, 'x');
selectFromFunction(() => example, 'x');
selectWithParameter((arg) => example, 'x');

这完全符合我的预期,所以我将使用它,但我仍然想知道原始版本失败的原因。

0 个答案:

没有答案