Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
192 views
in Technique[技术] by (71.8m points)

如何理解typescript的类型是否另一个类型的子集?

interface AT {
  // [k: string]: string // 加上这个就没问题了
  a: string
  b: string
}
const param: AT = {
  a: 'aa',
  b: 'bb'
}
f(param) // error ts2345
function f(a: {[k: string]: string}) {}
// function f(a: {[k: string]: any}) {} // 改这里也可以

请问为什么ts这么奇怪,该怎么理解?

按照我的理解,AT{[k: string]: string}的子集,应该不用额外声明的,就好像999可以传给number一样。

反而ts好像认为AT{[k: string]: any}的子集,为什么呢?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

好问题。

{a: string} 是不是 {[k:string]: string} 的子集?我们先看下面的代码:

interface T{
  a: string;
}

interface K{
  [k:string]: string;
}

interface U extends T {
  b: number;
}

// 这是合法的操作吧,这里定义的字面量符合 type U,自然可以
// 赋值给 type T 的类型
let foo:U = {a:'hello', b: 1};
let bar:T = foo; 

// 而如果 T 类型是 K 类型的子集,那么意味着这个操作也是
// 合法的,但是这明显不符合 K 的类型定义了啊?
let bzz:K = bar; 

// TS 如何处理这尴尬局面? type Q 是啥?string 还是 number?
type Q = typeof bzz['b'];

如上,如果规定{a: string}{[k:string]: string} 的子集,那么会出现子类型与父类型冲突的情况。事实上,这种类型定义叫做索引签名(index signature),而官方文档也是有明文规定的,定义了索引签名的接口允许定义其他具名属性,但是类型必须和索引签名相同。即:

interface K{
  [key: string]: string;
  name: string; // ok
}

interface P extends K {
  b: number; // wrong
  c: string; // ok
}

显然,{a: string,b:string} 不是 {[k:string]: string} 的子集,因为前者可以有无限的扩展可能,而后者存在扩展限制。而 {[key:string]:any}就没有问题,本质上它和object类型是一回事。即题目中的AT确实是{[key:string]:any}的子集。


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...