Typescript约束模板

问题描述 投票:0回答:2

我创建了这个HashMap界面:

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

我认为可以将它用作:

const map: HashMap<String, String>;

但是,现在我得到一个错误,说name只能是stringnumber类型。

如何在K模板上应用约束?

javascript typescript
2个回答
2
投票

首先,不要使用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
};

基本上,对于所有意图和目的,您必须使用字符串,但您可以使用union类型将键类型约束为特定字符串或一组特定字符串。

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

例如:

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


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

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

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


现在,这可能不是你想要的。如果要使用任意键类型,受某些约束限制,则需要使用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]);
  }
});

0
投票

由于你通常可以用作indexable types键的唯一类型是原始类型stringnumber,我不认为从它们参数化的能力上获得了很多,但是如果你坚持......

type IndexableTypes<V> = {
    [s: string]: { [n: string]: V };
    number: { [n: number]: V };
}

type SN = { [n: number]: 'number', [s: string]: string };


type HashMap<K extends string | number, V> = IndexableTypes<V>[SN[K]];

const map: HashMap<string, string> = {}; // inferred as {[n: string]: string; }
const map1: HashMap<number, string> = {}; // inferred as  {[n: number]: string;}

const map2: HashMap<number, HashMap<string, number>> = {};
// inferred as  
{[n: number]: 
  {[n: string]: number;};
}

const map4: HashMap<boolean, {}> = {};
// error: Type 'boolean' does not satisfy the constraint 'string | number'.

但是,如果它在您的运行时可用,您可以使用es2015 Map类:

type MyMap<K extends string | number, V> = Map<K, V>
© www.soinside.com 2019 - 2024. All rights reserved.