javascript - typescript 约束模板

我创建了这个HashMap接口:

export interface HashMap<K, V> {
    [name: K]: V;
}


我可以将其用作:

const map: HashMap<String, String>;


但是,现在我收到一条错误消息,说 name只能是 stringnumber类型。

如何在此处将约束应用于 K模板?

最佳答案

首先,不要使用UpperCase基本类型名称。也就是说使用string代替String

关于问题的实质,TypeScript索引签名的参数类型必须为stringnumbersymbol之一。最后一个仅在--target es2015或更高版本下可用。

但是,您可以将键的类型限制为字符串值的子集。在这种情况下,您必须使用type别名声明,而不是interface

export type HashMap<K extends string, V> = {
  [P in K]?: V;
}


[P in K]语法的意思是“枚举实例化 K的字符串类型子集中的每个字符串文字类型”。

这很有用,因为我们可以通过指定以字符串文字类型作为其组成部分的并集类型来限制地图的内容。

例如:

const map: HashMap<'firstName' | 'lastName', string> = {
  firstName: 'John', // OK
  nickname: 'Johnny' // Error
};


基本上,出于所有意图和目的,必须使用字符串,但是可以通过使用联合类型将键类型限制为特定字符串或一组特定字符串。

实际上,字符串联合类型通常是另一个类型的函数。

例如:

interface Item {
  name: string;
  id: number;
}


interface PropertyMetadata {
  kind: 'data' | 'accessor';
}

type LazyItem = {
  [P in keyof Item]: PropertyDescriptor
};


keyof是一个类型运算符,它接受一个类型并返回一个类型,该类型是其属性键的字符串联合。



现在,这可能不是您想要的。如果要使用受某些约束约束的任意键类型,则需要使用ES2015 Map对象。在JavaScript中添加此类型之前,不可能以干净的方式执行此映射,并且 string本质上是唯一可行的键类型。

通过结合使用ES2015映射和TypeScript泛型(称为模板)的功能,我们可以近似地找到您想要的内容。

例如:

interface Category {
  name: string;
  products: Product[];
}

interface Product {
  name: string;
  category: Category;
}

const categoriesToProducts = new Map<Category, Product[]>();

declare function getProducts(): Product[];

const products = getProducts();

products.forEach(product => {
  const mapped = categoriesToProducts.get(product.category);
  if (mapped) {
    mapped.push(product);
  } 
  else {
    categoriesToProducts.add(product.category, [product]);
  }
});