打字稿:使用键与值类型相同的映射,但不限制键的类型

时间:2018-07-28 16:40:19

标签: typescript dictionary generics

有没有办法进行类型检查?

array(
        'class'=>'CFileLogRoute',
        'levels'=>'error, warning, info',
        // Change categories as array
        'categories'=> array(
            'system.*'
         ),
        'logPath'=> '/export/home/sasori/branch/logs/',
        'logFile'=>'error.log',
    ),

使用默认值

const map = new Map()

map.set('stringkey', 'stringvalue') // OK
map.set(5, 10) // OK
const objectKey = { name: 'bob' }
map.set(objectKey, { name: 'alice' }) // OK

const stringValue: string = map.get('stringkey') // OK, but is typed as 'any'
const objectValue: { name: string } = map.get(objectKey) // OK, but is typed as 'any'

map.set('string', 5) // Error
map.set({ name: 'bob' }, { food: 'cake' }) // Error

可以使用,但是在访问值时不会给出有用的类型,因为它们都是任意键入的。

1 个答案:

答案 0 :(得分:4)

这很有趣。我认为最好的方法是声明自己的接口,该接口代表Map的特定变体:

interface SameKeyValueMap {
  clear(): void;
  delete(key: any): boolean;
  forEach(
    callbackfn: <T>(value: T, key: T, map: SameKeyValueMap) => void, thisArg?: any
  ): void;
  get<T>(key: T): T | undefined;
  has(key: any): boolean;
  set<T>(key: T, value: T): this;
  readonly size: number;
}

const map: SameKeyValueMap = new Map();

主要是以您想要的方式工作:

map.forEach((x, y) => (console.log(x === y))); // OK
map.set('stringkey', 'stringvalue') // OK
map.set(5, 10) // OK
const objectKey = { name: 'bob' }
map.set(objectKey, { name: 'alice' }) // OK

由于get()可以返回undefined,因此必须更改以下内容:

const stringValue: string | undefined = map.get('stringkey')
const objectValue: { name: string } | undefined = map.get(objectKey)

这是您想要的错误:

map.set('string', 5) // Error

但以下不是错误:

map.set({ name: 'bob' }, { food: 'cake' }) // accepted?

如果您看到的话,那是因为类型T被推断为{name: string, food?: undefined} | {name?: undefined, food: string}。并且 与您传入的值一致。这样,类型推断就有点荒唐了。

一种处理该问题的方法是将set()的一个推理站点的T签名更改为lower the priority。这样T只能从参数之一而不是两个参数中推断出来。像这样:

interface SameKeyValueMap {
  clear(): void;
  delete(key: any): boolean;
  forEach(
    callbackfn: <T>(value: T, key: T, map: SameKeyValueMap) => void, thisArg?: any
  ): void;
  get<T>(key: T): T | undefined;
  has(key: any): boolean;
  set<T>(key: T, value: T & {}): this; // lowered priority of value
  readonly size: number;
}

这似乎可行:

map.set({ name: 'bob' }, { food: 'cake' }) // error

在这种情况下,T被推断为{name: string},而{food: 'cake'}是不可分配的。所以这是一个错误。


希望有帮助。祝你好运!