microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.75k stars 12.46k forks source link

Order of ReadonlySet/ReadonlyMap in union causes differing key inference #52313

Open jakebailey opened 1 year ago

jakebailey commented 1 year ago

Bug Report

πŸ”Ž Search Terms

ReadonlyMap ReadonlySet key inference

πŸ•— Version & Regression Information

⏯ Playground Link

Playground Link

πŸ’» Code

declare const map: Map<string, number>;

declare function forEachKey<K, T>(map: ReadonlySet<K> | ReadonlyMap<K, any>, callback: (key: K) => T | undefined): T | undefined;

forEachKey(map, (key) => {}) // Error passing "map", key type is wrong (number??)

forEachKey<string, void>(map, (key) => {}) // OK; no error, key type is string

// Now, flip the union order in the parameter's type.
declare function forEachKey2<K, T>(map: ReadonlyMap<K, any> | ReadonlySet<K>, callback: (key: K) => T | undefined): T | undefined;

forEachKey2(map, (key) => {}) // Okay?

forEachKey2<string, void>(map, (key) => {}) // Still okay.

πŸ™ Actual behavior

The order of the union appears to matter, breaking inference and erroring on valid code. Swapping the union order allows the code to compile.

πŸ™‚ Expected behavior

All of this should compile; the union ordering should not matter.

typescript-bot commented 12 months ago

:wave: Hi, I'm the Repro bot. I can help narrow down and track compiler bugs across releases! This comment reflects the current state of the repro in the issue body running against the nightly TypeScript.


Issue body code block by @jakebailey

:x: Failed: -

Historical Information
Version Reproduction Outputs
4.8.2, 4.9.3, 5.0.2, 5.1.3, 5.2.2

:x: Failed: -

  • Argument of type 'Map' is not assignable to parameter of type 'ReadonlySet | ReadonlyMap'. Type 'Map' is not assignable to type 'ReadonlyMap'. Types of property 'forEach' are incompatible. Type '(callbackfn: (value: number, key: string, map: Map) => void, thisArg?: any) => void' is not assignable to type '(callbackfn: (value: any, key: number, map: ReadonlyMap) => void, thisArg?: any) => void'. Types of parameters 'callbackfn' and 'callbackfn' are incompatible. Types of parameters 'key' and 'key' are incompatible. Type 'string' is not assignable to type 'number'.