typescript 使用字符串变量作为属性名的动态类型

sd2nnvve  于 2023-04-22  发布在  TypeScript
关注(0)|答案(2)|浏览(218)

I would like to be able to construct a type dynamically with a property name which is one of the specified parameters. While I can construct the actual object, I cannot seem to construct the actual type. I would like to use this type for composition alter

export function mapProp<AssignedType>(value: AssignedType, propertyName: string) {

  type ReturnType = {
    [propertyName]: value
  }; // errors on this line

  return {
    [propertyName]: value
  };
}

The error emitted is as follows:
A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type.
Example playground: http://www.typescriptlang.org/play/#src=%0D%0Aexport%20function%20mapProp%3CAssignedType%3E(value%3A%20AssignedType%2C%20propertyName%3A%20string)%20%7B%0D%0A%0D%0A%20%20type%20ReturnType%20%3D%20%7B%0D%0A%20%20%20%20%5BpropertyName%5D%3A%20value%0D%0A%20%20%7D%3B%0D%0A%0D%0A%20%20return%20%7B%0D%0A%20%20%20%20%5BpropertyName%5D%3A%20value%0D%0A%20%20%7D%3B%0D%0A%7D

wz8daaqr

wz8daaqr1#

我想你能得到的最接近的结果是这样的:

export function mapProp<PropertyName extends string, AssignedType>(
  value: AssignedType, 
  propertyName: PropertyName
) {

  type ReturnType = {
    [K in PropertyName]: AssignedType
  }; 
  // equivalent to Record<PropertyName, AssignedType>

  return {
    [propertyName]: value
  } as ReturnType;

}

在这种情况下,您将使用Map类型而不是具有索引签名的类型。添加PropertyName泛型类型参数允许缩小string之后的键,如果您向其传递字符串文字:

const thing = mapProp(123, "abc");
thing.abc; // number
thing.def; // error

在这种情况下,已知ReturnType等价于{abc: number}。如果你只知道在编译时键是string,那么你会得到:

declare const propName: string;
const stuff = mapProp(123, propName);
stuff.abc; // number
stuff.def; // number

现在ReturnType等价于{[k: string]: number},这意味着它接受任何string键(并给它一个number值)。这可能不是你想要的,但这是编译器在这种情况下能做的最好的事情。
还要注意的是,如果不使用类型Assert(as ReturnType),计算属性通常最终会成为字符串索引,而不是更具体的东西。这是目前TypeScript的design limitation。已经有一些attempts来处理这个问题,但还没有任何东西进入语言。
希望有帮助;祝你好运!

2ledvvac

2ledvvac2#

也就是说,有更优雅和简单的方法来实现这一点(参见https://www.typescriptlang.org/docs/handbook/2/mapped-types.html)。
因此,我们现在可以像这样声明类型,而不是使用函数返回类型:

export type MappedType<PropertyName extends string, AssignedType> = {
    [K in PropertyName]: AssignedType
};

let mapped: MappedType<'foo', boolean>;
mapped = {foo: true};  // OK
mapped = {foo: 123}; // error: Type 'number' is not assignable to type 'boolean'
mapped = {foo: true, bar: 123}; // error: 'bar' does not exist in type ...

// You can also define multiple type-properties and use them like variables
type MultipleTypes = [
    {prop: 'p_bool'; value: boolean},
    {prop: 'p_str'; value: 'abc'}
];

type FirstType = MultipleTypes[0];
let m: MappedType<FirstType['prop'], FirstType['value']>;
m = {p_bool: true}; // OK
m = {p_bool: 123};  // errror, wrong type

或者你甚至可以基于另一个字符串动态地创建属性:

type Langs = 'en' | 'de';
export type TranslationsType<PropertyName extends string> = {
    [K in `${PropertyName}_${Langs}`]: string
};

const test: TranslationsType<'bla'> = {
    bla_de: 'Hallo',
    bla_en: 'Hello', // if left out, test would have error: Property 'bla_en' is missing in type... 
    bla_es: 'Hola',  // error 'bla_es' does not exist ....
};

相关问题