1

我想创建一个 TypeScript 函数,该函数接受一个对象和该对象内的一个属性,其值为 a string。使用<T, K extends keyof T>工作来确保只T允许键作为属性的值,但我似乎无法缩小范围,以便键也必须指向 type 的属性string。这可能吗?

我试过这个:

function getKey<T extends {K: string}, K extends keyof T>(item: T, keyProperty: K): string {
     return item[keyProperty];
}

但它只是说Type 'T[K]' is not assignable to type 'string'。为什么T extends {K: string}约束不能确保它T[K]实际上是 a string,或者更确切地说,提供的K必须满足条件,所以它T[K]是 a string

为了清楚起见,我希望能够像这样调用这个函数:

getKey({foo: 'VALUE', bar: 42}, 'foo') => return 'VALUE';

getKey({foo: 'VALUE', bar: 42}, 'bar') => should not be allowed since 'bar' is not a string property of the supplied object

getKey({foo: 'VALUE', bar: 'ANOTHER VALUE'}, 'bar') => return 'ANOTHER VALUE'
4

2 回答 2

2

in, {K: string}, Kin 只是字符串文字键的名称。就像你写的一样{"K": string}

type Oops = { "K": string };
// type Oops = { K: string; }

由于您想K成为键的类型,因此您需要使用映射类型,它迭代一些键的联合......或者,或者使用实用程序类型{[P in K]: string}的等价物:Record<K, string>Record<K, T>

function getKey<T extends { [P in K]: string }, K extends keyof T>(
    item: T,
    keyProperty: K
): string {
    return item[keyProperty]; // no error
}

并且您的调用代码的行为(大部分)如您所料:

getKey({ foo: 'VALUE', bar: 42 }, 'foo'); // okay
getKey({ foo: 'VALUE', bar: 42 }, 'bar'); // error!
//   ----------------> ~~~
// number is not assignable to string
getKey({ foo: 'VALUE', bar: 'ANOTHER VALUE' }, 'bar'); // okay

我说“大部分”是因为您可能希望第二行中的错误出现在'bar'传入的值上 for keyProperty,而实际发生的是错误出现在bar传入的值的属性上 for item

再多花点功夫,你就可以做到这一点:

type KeysMatching<T, V> = { [K in keyof T]: T[K] extends V ? K : never }[keyof T];

function getKey2<T extends { [P in K]: string }, K extends KeysMatching<T, string>>(
    item: T,
    keyProperty: K
): string {
    return item[keyProperty];
}

在这里,我们K不仅限制为,而且限制其值为 typekeyof T的特定键。我们使用自己的实用程序类型来做到这一点。这不会改变最终有效的值,但它会改变编译器在某些内容无效时抱怨的位置:TstringKeysMatching<T, V>

getKey2({ foo: 'VALUE', bar: 42 }, 'foo'); 
getKey2({ foo: 'VALUE', bar: 42 }, 'bar'); // error!
//  -----------------------------> ~~~~~
//  "bar" is not "foo"
getKey2({ foo: 'VALUE', bar: 'ANOTHER VALUE' }, 'bar');

好的,希望有帮助;祝你好运!

Playground 代码链接

于 2020-05-01T00:32:23.437 回答
1
function getKey<T>(item: T, keyProperty: {[K in keyof T]: T[K] extends string ? K : never}[keyof T]): string {
    return <string> <unknown> item[keyProperty];
}

有点笨重,但是嗯..它的工作原理。:)

于 2020-04-30T18:52:43.203 回答