TypeScript:接受映射到特定类型的所有对象键

TypeScript:接受映射到特定类型的所有对象键,typescript,types,Typescript,Types,给定一个对象类型(或类类型),我想编写一个函数来接受对象及其键列表。但是,我只希望允许映射到特定类型的值的键,例如仅允许字符串 例如: 函数应仅接受字符串值(o,键){ //根据o[key]具有特定类型(例如字符串)的假设,使用o[key]执行某些操作 } 常量对象={ 答:1,, b:“测试”, c:“布拉” } const key=“c”作为常量; 应仅接受字符串值(对象,键);//b和c应被接受为键,a不应被接受。 我知道有一种方法可以强制执行键实际上存在于o上(无论o[key]的类型如

给定一个对象类型(或类类型),我想编写一个函数来接受对象及其键列表。但是,我只希望允许映射到特定类型的值的键,例如仅允许字符串

例如:

函数应仅接受字符串值(o,键){
//根据o[key]具有特定类型(例如字符串)的假设,使用o[key]执行某些操作
}
常量对象={
答:1,,
b:“测试”,
c:“布拉”
}
const key=“c”作为常量;
应仅接受字符串值(对象,键);//b和c应被接受为键,a不应被接受。
我知道有一种方法可以强制执行
实际上存在于
o
上(无论
o[key]
的类型如何):

函数应仅接受字符串值(o:T,key:keyof T){
//根据o[key]具有特定类型(例如字符串)的假设,使用o[key]执行某些操作
}
但是,这也允许使用
key=“a”
,尽管它映射到一个数字

我需要的是这样的东西:

函数应仅接受字符串值(o:T,键:K)
但这当然不是有效的TypeScript代码


有什么诀窍可以让它起作用吗?我需要一种方法来进一步优化键集
keyof T
。然后,函数体应该知道
o[key]
是一个字符串,而不需要显式检查函数内部的类型。这是可能的吗?

如果您希望从调用方和实现方的角度都能工作,您可以这样做:

function shouldOnlyAcceptStringValues<K extends PropertyKey>(
  o: Record<K, string>, key: K
) {
    const okay: string = o[key];
}
唯一的障碍是第一次调用的错误可能不在您期望的参数上;它在抱怨
obj
,而不是
“a”
。如果可以的话,很好。如果没有,那么您可以将调用签名更改为您正在讨论的约束类型:


type KeysMatching={[K in keyof T]:T[K]扩展V?K:never}[keyof T]
函数应仅接受StringValues2(o:T,键:KeyMatching):无效;
函数应仅接受StringValues2(
o:记录,键:K
) {
常量OK:string=o[key];
}
KeysMatching
type函数采用类型
T
,只返回那些值可分配给
V
的键。因此,调用签名将为
o
指定
T
,为
key
指定
KeysMatching
。请注意,我是如何将调用签名作为单个签名编写的,实现签名与以前相同。如果不这样做,则编译器无法理解对于泛型
t
t[KeysMatching]
可分配给
string
;这是编译器无法进行的高阶类型推断:

函数应仅接受StringValuesOps(o:T,键:键匹配){
常量oops:string=o[key];//错误!
// -> ~~~~
//类型“T[{[K in keyof T]:T[K]扩展字符串?K:never;}[keyof T]]

//不可分配给类型“字符串”。(非OP)阅读你的答案总是一种澄清的乐趣。谢谢。我有一些问题。第一个问题是关于第一个解决方案:通过使用
o:Record
,我不是只接受只有映射到字符串的键的对象吗?显然,你的代码是有效的,所以显然我的假设是错误的,但我真的不明白为什么。是
Record一种超类型的
,它解释了为什么只接受包含字符串值的对象。
记录
表示“对于
K
中的键,值是
字符串
”。它对不在
K
中的键没有任何影响。TypeScript中的对象类型不是这样的;它们被允许有额外的属性(尽管在某些情况下是这样做的)。因此
{a:number,b:string,c:string}
类型的值可分配给
{b:string}类型的变量
aka
Record
。您好,我想知道如何也接受
string | null
属性。虽然上述方法适用于
KeysMatching
和字符串值,但不适用于
KeysMatching
KeysMatching
,其中
Maybe=T | null
值不被接受。我尝试将其更改为
{[K in keyof T]-?:T[K]可能扩展?K:never;}[keyof T]
,但这也不起作用。有什么想法吗?@Steinroe没有a很难说;
MyObject
是什么?我无法轻松复制,所以跟进问题:
shouldOnlyAcceptStringValues(obj, "a"); // error!
// ------------------------> ~~~
// Argument of type '{ a: number; b: string; c: string; }' is 
// not assignable to parameter of type 'Record<"a", string>'.

shouldOnlyAcceptStringValues(obj, "b"); // okay
shouldOnlyAcceptStringValues(obj, "c"); // okay
type KeysMatching<T, V> = { [K in keyof T]: T[K] extends V ? K : never }[keyof T]
function shouldOnlyAcceptStringValues2<T>(o: T, key: KeysMatching<T, string>): void;
function shouldOnlyAcceptStringValues2<K extends PropertyKey>(
  o: Record<K, string>, key: K
) {
    const okay: string = o[key];
}