简体   繁体   English

为什么 TypeScript 不抱怨返回类型不正确?

[英]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
}

Playground link 游乐场链接

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:我的问题是:

  1. 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
  2. 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;

Playground操场

I have created an issue in TS repo我在 TS repo 中创建了一个问题

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类型具有可选属性idname ,而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分配给Itemstring[]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上的官方说法是什么,然后才能确定。

Playground link to code Playground 代码链接

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Typescript 泛型函数不会抱怨回调返回类型 - Typescript generic function doesn't complain about callback return type 为什么TypeScript不抱怨带有计算键的Partial类型中的值不正确? - Why doesn't TypeScript complain about incorrect values in Partial types with computed keys? 从concat构建时,为什么TypeScript不会抱怨数组的类型? - Why doesn't TypeScript complain about an array's type when built from concat? Typescript不会抱怨缺少导入 - Typescript doesn't complain about missing imports 为什么 TypeScript 不抱怨由 Object.create 导致的接口违规? - Why doesn't TypeScript complain about interface violations resulting from Object.create? TypeScript 不应该抱怨我分配了错误的类型吗? - Shouldn't TypeScript complain about me assigning a wrong type? 类型上不存在属性:为什么 TypeScript 抱怨而 JavaScript 不抱怨? - Property does not exist on type: Why does TypeScript complain while JavaScript doesn't? 当我忽略泛型类型定义时,为什么打字稿不会抱怨? - Why doesn't typescript complain when I ignore generic type definitions? 为什么它抱怨类型 {intrinsicattributes &amp;&amp; true} 或类型 {intrinsicattributes &amp;&amp; false} 不能使用 react 和 typescript 分配? - Why does it complain about type {intrinsicattributes && true} or type {intrinsicattributes && false} are not assignable using react and typescript? TypeScript编译器不会抱怨此通用约束 - TypeScript compiler doesn't complain with this generic constraint
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM