TypeScript:获取对象本身内部的对象键的类型

时间:2018-07-24 15:39:04

标签: typescript

首先,对不起,标题不好。我真的不知道如何描述问题,也许这就是为什么我还没有找到解决方案的原因!

这里有一个小片段说明了我的问题:

type Type<T> = {
  key: keyof T,
  doStuff: (value: T[typeof key]) => void
//                          ^^^
// TS2034: Cannot find name 'key'.
};

我想做的事情非常简单(我希望)。我已经尝试解决了几次,但是每次参数最终都是所有可用类型的并集。

const Test: Type<{ var1: string, var2: number }> = {
  key: 'var1',
  doStuff: (value) => {}
//          ^^^^^
// (parameter) value: string | number
};

如果有人可以帮助我,我将非常感激。如果您需要有关我在这里尝试做的事情或已经在尝试做的事情的其他信息,请告诉我!

2 个答案:

答案 0 :(得分:3)

TypeScript中没有内置的existential types,因此您不能说“我不在乎哪个键key,但它必须 some T”。

执行此操作的唯一方法是使TypeT中的K extends keyof T通用,如下所示:

type Type<T, K extends keyof T> = {
  key: K,
  doStuff: (value: T[K]) => void
};

然后您可以这样指定Test

const Test: Type<{ var1: string, var2: number }, "var1"> = {
  key: 'var1',
  doStuff: (value) => { } // value is inferred as string
}

这可行,但是您可能不满意必须在type和"var1"属性中手动指定key。不幸的是,您不能仅指定T并留下K来推断at least for now。最终,应该有一种获取partial type argument inference的方法,可能最早到August 2018, for TypeScript 3.1

现在,您可以解决涉及currying的变通方法,其中泛型函数返回泛型函数。您指定一个,而另一个则被推断。像这样:

const typeFor = <T>() => <K extends keyof T>(type: Type<T, K>) => type;

// T is specified manually
const typeForVar1StringVar2Number = typeFor<{ var1: string, var2: number }>();

// K is inferred from the argument
const Test2 = typeForVar1StringVar2Number({
  key: 'var1',
  doStuff: (value) => { } // value is inferred as string
});

涉及更多点,但确实可以避免为'var1'写出K

好的,希望能有所帮助。祝你好运!


编辑:我看到您确实需要存在类型,因为您需要这些东西的数组。当您具有文字的并集(例如keyof T如果T没有字符串索引)时,获取类存在类型的一种方法是使用distributive conditional types

type PossibleTypes<T> = keyof T extends infer K ? 
  K extends any ? Type<T, K> : never : never;

PossibleTypes<T>成为Type<T, K>中每个K的所有keyof T的并集。让我们用它来制作该数组:

type ArrayOfPossibleTypes<T> = Array<PossibleTypes<T>>
const asArrayOfPossibleTypes = <T>(arr: ArrayOfPossibleTypes<T>) => arr;
const testArray = asArrayOfPossibleTypes<{ var1: string, var2: number }>([
  {
    key: 'var1', doStuff(value) { /* value is string */ }
  }, {
    key: 'var2', doStuff(value) { /* value is number */ }
  }
]);

那里的推论看起来不错。如果您不太担心分布式条件类型,那可以说对您有用。


如果所有其他方法均失败,则在TypeScript中存在 类型的实现,但是它涉及continuation passing,这对于您的用例而言可能太多了。为了完整性,我将为您提供一个示例:

type ExistentialType<T> = <R>(f: <K extends keyof T>(x: Type<T, K>) => R) => R;

不是说“我是Type<T, K>的{​​{1}}”,而是说“如果给我一个可以在任何K上运行的函数,我可以将该函数称为你,给你结果”。让我们创建一个:

Type<T, K>

然后使用它:

const exType: ExistentialType<{ var1: string, var2: number }> = 
  (f) => f({ key: 'var1', doStuff(value) { } });

这种由内而外的特性既古怪又有趣,偶尔有用……但在这里可能矫kill过正。


好的,希望 有帮助。再次祝你好运。

答案 1 :(得分:0)

如果您不介意将键放在类型参数中:

export type Type<T, V extends keyof T> = {
    key: V, // not required
    doStuff: (value: T[V]) => void
};

const Test: Type<{ var1: string, var2: number }, "var1"> = {
    key: "var1", // not required
    doStuff: (value): void => {
        // value is type string
    }
};