[英]Why doesn't TypeScript complain about an incorrect return type?
Please consider this example:请考虑这个例子:
// all properties in Item should be optional, this is by design
type Item = {
id?: number
name?: string
}
interface WithVersion {
version: number
}
export type ResultType =
& WithVersion // #1 try to remove it
& {
version: number
list: Item[];
};
export interface Data {
list: Array<string>;
}
// As per my understanding, this function should not compile, because elem.list is not assignable to list in ResultType
const builder = <T extends Data>(data: Array<T>): ResultType[] => {
const result = data.map((elem) => ({
list: elem.list, // #2 list is string[], whereas ResultType expects list to be Item[]
version: 2,
}))
return result // #3
}
I am a bit confused by the assignability rules of TypeScript.我对 TypeScript 的可分配性规则有点困惑。
Try to remove WithVersion
from ResultType
.尝试从
ResultType
WithVersion
TypeScript will complain about the assignability of result
to return type of builder
function ( ResultType
). TypeScript 将抱怨
result
可分配给builder
function ( ResultType
) 的返回类型。
Further more, if you define explicit return type for Array.prototype.map
callback, TypeScript will complain just as I expect:此外,如果您为
Array.prototype.map
回调定义显式返回类型,TypeScript 会像我预期的那样抱怨:
const builder = <T extends Data>(data: Array<T>): ResultType[] => {
const result = data.map((elem):ResultType => ({
list: elem.list, // error as expected
version: 2,
}))
return result
}
My questions are:我的问题是:
Why there is no error without explicit type for map
callback.为什么没有显式类型的
map
回调没有错误。 It is clear that string[]
is not assignable to Item[]
.很明显
string[]
不能分配给Item[]
。
declare let foo: string[] declare let bar: Item[] foo = bar bar = foo
Why does an error appear when I remove WithVersion
from the ResultType
definition?当我从
ResultType
定义中删除WithVersion
时,为什么会出现错误? It looks like that intersection of WithVersion
and { version: number list: Item[]; }
看起来
WithVersion
和{ version: number list: Item[]; }
的交集{ version: number list: Item[]; }
somehow affects ResultType
, whereas in my opinion, this intersection should not affect it at all. { version: number list: Item[]; }
以某种方式影响ResultType
,而在我看来,这个交叉点根本不应该影响它。
SIMPLIFIED VERSION简化版
type Item = {
id?: number
name?: string
}
interface WithVersion {
version: number
}
export type ResultType =
& WithVersion // #1 try to remove it
& {
version: number
list: Item[];
};
declare let result: ResultType;
declare let list: string[];
let a = {
list,
version: 2,
};
result = a;
TL;DR Weak type detection doesn't always occur for intersection types ; TL;DR 交集类型并不总是发生弱类型检测; this seems to be behaving as designed, although the particular behavior you're seeing might be a design limitation.
尽管您看到的特定行为可能是设计限制,但这似乎与设计一致。
From a purely structural standpoint, string
is indeed assignable to Item
.从纯粹结构的角度来看,
string
确实可以分配给Item
。 The Item
type has optional properties id
and name
, and values of type string
are missing these properties, so there's no apparent conflict. Item
类型具有可选属性id
和name
,而string
类型的值缺少这些属性,因此没有明显的冲突。 Reading "foo".id
or "bar".name
gives you undefined
in both cases.阅读
"foo".id
或"bar".name
在这两种情况下都会给你undefined
。 If TypeScript only cared about structural compatibility, then none of your examples would have compiler errors.如果 TypeScript 只关心结构兼容性,那么您的示例都不会出现编译器错误。
Of course, it is probably a mistake to assign a string
value to something that expects an Item
.当然,将
string
值分配给需要Item
的东西可能是错误的。 TypeScript has several features to try to catch these sort of non-type-safety mistakes. TypeScript 有几个功能可以尝试捕捉这些非类型安全错误。 These are more like linter warnings than type errors.
这些更像是 linter 警告而不是类型错误。
One such feature is weak type detection .其中一项功能是弱类型检测。 A weak type is an object type whose properties are all optional, like
Item
.弱类型是 object 类型,其属性都是可选的,例如
Item
。 Weak type detection causes the compiler to complain if you try to assign something to a weak type if there is no overlap in properties.如果属性没有重叠,如果您尝试将某些内容分配给弱类型,弱类型检测会导致编译器抱怨。 (Aside: a more well-known such feature is excess property checking , in which object literals are not allowed to have unexpected properties.) Weak type detection is why you will get a warning if you assign a
string
to an Item
or a string[]
to an Item[]
or an {x: string; y: string}
(除此之外:一个更广为人知的此类功能是过度属性检查,其中 object 文字不允许具有意外属性。)弱类型检测是您将
string
分配给Item
或string[]
到Item[]
或{x: string; y: string}
{x: string; y: string}
to an {x: Item; y: string}
{x: string; y: string}
到{x: Item; y: string}
{x: Item; y: string}
: {x: Item; y: string}
:
const foo = { x: "", y: "" }
const bar: { x: Item; y: string } = foo; // error
So then: why don't you get a warning when you try to assign an {x: string, y: string}
to an {x: Item} & {y: string}
?那么:为什么在尝试将
{x: string, y: string}
分配给{x: Item} & {y: string}
时没有收到警告?
const baz: { x: Item } & { y: string } = foo; // no error?!
Why is there a difference with intersection types ?为什么交叉口类型有区别?
Well, according to microsoft/TypeScript#16047 , the pull request that implemented weak type detection, weak type detection does not occur in intersection types unless all the intersected types are weak types.好吧,根据microsoft/TypeScript#16047 ,实现弱类型检测的拉取请求,弱类型检测不会发生在交集类型中,除非所有交集类型都是弱类型。 Since
{y: string}
is not a weak type (and technically neither is {x: Item}
because the x
property is not optional), then the intersection does not undergo weak type checking at all.由于
{y: string}
不是弱类型(从技术上讲, {x: Item}
也不是,因为x
属性不是可选的),所以交集根本不进行弱类型检查。 Therefore things fall back to the normal structural type check, and the assignment succeeds.因此事情会退回到正常的结构类型检查,并且分配成功。
This still raises the question of why weak type detection was implemented so that intersections are usually exempt.这仍然提出了为什么要实施弱类型检测以使交叉路口通常被豁免的问题。 I don't really see this explicitly documented, but it looks like there was a prior version of this feature implemented at microsoft/TypeScript#3842 where weak type detection for intersections caused undesirable behavior.
我并没有真正看到这一点明确记录,但看起来在microsoft/TypeScript#3842中实现了此功能的先前版本,其中对交叉点的弱类型检测会导致不良行为。 My presumption here is that the intent was to be somewhat conservative and only emit errors in cases known to be bad, and there are some intersection situations we want to accept (maybe generics?).
我在这里的假设是,意图有点保守,只在已知不好的情况下发出错误,并且我们想要接受一些交叉情况(可能是 generics?)。
In any case, this is definitely behaving as designed.无论如何,这绝对是按设计行事的。 It might be a design limitation.
这可能是设计限制。 I suppose we will wait to see what the official word is on microsoft/TypeScript#50608 before knowing for sure.
我想我们会等着看microsoft/TypeScript#50608上的官方说法是什么,然后才能确定。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.