typescript 如何使用对象的字符串文字属性作为同一对象中的键类型

qmb5sa22  于 2023-10-22  发布在  TypeScript
关注(0)|答案(2)|浏览(168)

例如,我有一些接口:

interface Foo {
  bar: string;
  baz: (param: any) => void;
}

和对象:

const test = { 
  bar: 'id', 
  baz: (param) => console.log(param.id),
};

如何强制TypeScript使用bar值作为paramkey类型,而不使用Map类型?所以我想有这样的东西:

interface Foo<J> {
  bar: J;
  baz: (param: {[key: J]: any}) => void;
}

和对象:

const test = { 
  bar: 'id', 
  baz: ({id}) => console.log(id), // <== here should typescript allow only the id property
};

我知道我可以使用Map类型来实现这种行为,但这不是我想要的

l0oc07j2

l0oc07j21#

TypeScript中没有 specific 类型Foo,它是这样工作的。最接近的方法是将您的需求表示为generic类型Foo<K>,其中K对应于bar属性。它非常接近于你所拥有的,除了你试图使用一个索引签名{[key: K]: any},这对泛型无效。你需要一个mapped type{[P in K]: any}

interface Foo<K extends PropertyKey> {
  bar: K;
  baz: (param: { [P in K]: any }) => void;
}

不幸的是,编译器不能将类型参数推断为泛型类型,所以你不能写const test: Foo<infer> = {⋯}const test: Foo<?> = {⋯}const test: Foo = {⋯}或类似的东西。编译器可以在你调用泛型函数时推断出类型参数,所以你可以创建一个助手函数:

const asFoo = <K extends PropertyKey>(foo: Foo<K>) => foo;

然后像const test = asFoo({⋯})一样使用它:

const test = asFoo({
  bar: 'id',
  baz: ({ id }) => console.log(id), // okay
});

const test2 = asFoo({
  bar: 'id',
  baz: ({ oops }) => console.log(oops), // error 
});

Playground链接到代码

sgtfey8w

sgtfey8w2#

在TypeScript中,要在baz函数的param对象中使用bar值作为键类型而不使用Map类型,可以通过使用索引访问类型定义具有动态键的param类型来实现。你可以这么做

interface Foo<J extends string> {
  bar: J;
  baz: (param: { [key in J]: any }) => void;
}

const test: Foo<'id'> = {
  bar: 'id',
  baz: ({ id }) => console.log(id),
};

相关问题