打字稿函数,其中对象具有由另一个参数确定的 属性 类型的字符串

Typescript function where object has a property of type string determined by another parameter

我想创建一个 TypeScript 函数,它接受一个对象和该对象中的 属性,其值为 string。使用 <T, K extends keyof T> 可以确保只允许 T 的键作为 属性 的值,但我似乎无法缩小范围,因此键也必须指向 [=32] =] 类型 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] 实际上是 string,或者更确切地说,提供的 K 必须满足条件,以便 T[K]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'
function getKey<T>(item: T, keyProperty: {[K in keyof T]: T[K] extends string ? K : never}[keyof T]): string {
    return <string> <unknown> item[keyProperty];
}

有点笨重,但嗯..它有效。 :)

中,{K: string}中的K中只是string literal键的名称。这和你写 {"K": string}:

是一样的
type Oops = { "K": string };
// type Oops = { K: string; }

由于您希望 K 成为密钥的 类型 ,因此您需要使用 mapped type, which iterates over some union of keys... either {[P in K]: string}, or the equivalent Record<K, string> using the Record<K, T> utility type:

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

我说 "mostly" 是因为您可能希望第二行中的错误出现在为 keyProperty 传入的 'bar' 值上,而实际发生的是错误在为 item.

传入的值的 bar 属性 上

再花点心思就可以实现:

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 约束到 keyof T,还约束到 T 的特定键,其值是 string 类型。我们使用自己的 KeysMatching<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 link to code