是否强制使用Typescript对象的索引成员类型?

时间:2012-11-09 20:02:46

标签: typescript

我想存储字符串的映射 - >在Typescript对象中的字符串,并强制所有键映射到字符串。例如:

var stuff = {};
stuff["a"] = "foo";   // okay
stuff["b"] = "bar";   // okay
stuff["c"] = false;   // ERROR!  bool != string

我是否有办法强制执行值必须是字符串(或任何类型..)?

8 个答案:

答案 0 :(得分:276)

var stuff: { [s: string]: string; } = {};
stuff['a'] = ''; // ok
stuff['a'] = 4;  // error

// ... or, if you're using this a lot and don't want to type so much ...
interface StringMap { [s: string]: string; }
var stuff2: StringMap = { };
// same as above

答案 1 :(得分:69)

无论我走到哪里,我随身携带这个文件

export interface StringTMap<T> { [key: string]: T; };
export interface NumberTMap<T> { [key: number]: T; };

export interface StringAnyMap extends StringTMap<any> {};
export interface NumberAnyMap extends NumberTMap<any> {};

export interface StringStringMap extends StringTMap<string> {};
export interface NumberStringMap extends NumberTMap<string> {};

export interface StringNumberMap extends StringTMap<number> {};
export interface NumberNumberMap extends NumberTMap<number> {};

export interface StringBooleanMap extends StringTMap<boolean> {};
export interface NumberBooleanMap extends NumberTMap<boolean> {};

StringTMapNumberTMap是泛型,可用于创建任何类型的地图(通过let myTypeMap: StringTMap<MyType> = {})。其余的是常见文字类型之间有用的预定义映射。

这里的重要语法是{ [key: string]: T; },表示接口强制使用类型为string的键的对象文字(单词key可以是任何标识符,应该用于指示密钥的重要性)和T类型的值。如果你想构造一个string&#34;名称&#34;对于密钥和boolean值(并且不使用上面的继承),它的接口将是{ [name: string]: boolean }

10.10.2018更新: 查看下面的@dracstaxi答案 - 现在有一个内置类型Record,它或多或少地完成了这个。

答案 2 :(得分:30)

快速更新:自Typescript 2.1起,内置了Record<T, K>类型,其作用类似于字典。

文档示例:

// For every properties K of type T, transform it to U
function mapObject<K extends string, T, U>(obj: Record<K, T>, f: (x: T) => U): Record<K, U>

const names = { foo: "hello", bar: "world", baz: "bye" };
const lengths = mapObject(names, s => s.length);  // { foo: number, bar: number, baz: number }

TypeScript 2.1 Documentation on Record<T, K>

我在{[key: T]:K上使用此键的唯一缺点是,您可以编码有用的信息,以了解要使用哪种键代替“键”,例如如果您的对象只有主键,则可以这样提示:{[prime: number]: yourType}

这是我写来帮助实现这些转换的正则表达式。这只会转换标签为“键”的情况。要转换其他标签,只需更改第一个捕获组:

查找:\{\s*\[(key)\s*(+\s*:\s*(\w+)\s*\]\s*:\s*([^\}]+?)\s*;?\s*\}

替换:Record<$2, $3>

答案 3 :(得分:7)

@Ryan Cavanaugh的回答完全没问题且仍然有效。还有一点值得补充的是,在我们可以宣称ES6得到大多数平台支持的情况下,当我们需要将某些数据与某些密钥相关联时,坚持使用Map几乎总是更好。

当我们写let a: { [s: string]: string; }时,我们需要记住,在编写的打字稿之后,不是类型数据这样的东西,它只用于编译。并且{[s:string]:string;将编译为{}。

那就是说,即使你写了类似的东西:

class TrickyKey  {}

let dict: {[key:TrickyKey]: string} = {}

这只是不会编译(即使是target es6,你也会得到error TS1023: An index signature parameter type must be 'string' or 'number'.

所以实际上你只能使用字符串或数字作为潜在的密钥,所以这里没有那么强烈的类型检查意识,特别要记住,当js尝试按编号访问密钥时,它会将其转换为字符串。

因此,假设最佳做法是即使键是字符串也使用Map是非常安全的,所以我坚持:

let staff: Map<string, string> = new Map();

答案 4 :(得分:2)

您可以将名称传递给未知键,然后输入您的类型:

type StuffBody = {
  [key: string]: string;
};

现在您可以在类型检查中使用它:

let stuff: StuffBody = {};

但是对于 FlowType ,不需要名称:

type StuffBody = {
  [string]: string,
};

答案 5 :(得分:1)

在@ shabunc的答案的基础上,这将允许强制执行键或值 - 或两者 - 是你想要强制执行的任何事情。

type IdentifierKeys = 'my.valid.key.1' | 'my.valid.key.2';
type IdentifierValues = 'my.valid.value.1' | 'my.valid.value.2';

let stuff = new Map<IdentifierKeys, IdentifierValues>();

也可以使用enum代替type定义。

答案 6 :(得分:0)

定义界面

interface Settings {
  lang: 'en' | 'da';
  welcome: boolean;
}

强制将键设置为“设置”界面的特定键

private setSettings(key: keyof Settings, value: any) {
   // Update settings key
}

答案 7 :(得分:-1)

interface AccountSelectParams {
  ...
}
const params = { ... };

const tmpParams: { [key in keyof AccountSelectParams]: any } | undefined = {};
  for (const key of Object.keys(params)) {
    const customKey = (key as keyof typeof params);
    if (key in params && params[customKey] && !this.state[customKey]) {
      tmpParams[customKey] = params[customKey];
    }
  }

如果您对此概念有所了解,请发表评论