You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Refining discriminated union of arrays with array.find on a property that is a union #60785
interfaceSharp{// Removing 'Triangle' here to get rid of the union makes it work.type: 'Square'|'Triangle';corners: number;}interfaceRound{type: 'Circle'|'Oval';radius: number;}functiongetCorners(shapes: Sharp[]|Round[]){// Expected `Sharp | undefined` but got `Sharp | Round | undefined`constfoundSharp=shapes.find((shape)=>shape.type==='Square');// Property 'corners' does not exist on type 'Sharp | Round'.// Property 'corners' does not exist on type 'Round'. (2339)returnfoundSharp?.corners;}
🙁 Actual behavior
foundSharp is Sharp | Round | undefined even though it can never be Round due to type.
🙂 Expected behavior
foundSharp should be Sharp | undefined since only sharp shapes can have the type 'Square'.
Additional information about the issue
Setting type in the Sharp interface to only 'Square' makes it work as expected.
This isn't a TS bug. Your callback isn't inferred as returning a type predicate as implemented in #57465, because it would need to be a one-sided type predicate, which TypeScript doesn't have. The check shape.type === 'Square' can narrow Round | Sharp to Sharp but since shape.type !== 'Square' does not narrow Round | Sharp to Round, no type predicate can be inferred. (See flakyIsString in the text of #57465) If TS had one-sided type predicates as requested in #15048, then the return type could be inferred as something like shape is Shape else false and it would work. When you're using find() you don't care about the false side of the type guard function, but TS does, and there's currently no way to tell it not to.
If you want, you can annotate your callback with a type predicate return type, and while shape is Sharp will work, it's a bit of a lie. Something which works and is true is:
functiongetCorners(shapes: Sharp[]|Round[]){constfoundSharp=shapes.find((shape): shape is typeofshape&{type: "Square"}=>shape.type==='Square');returnfoundSharp?.corners;}
Activity
jcalz commentedon Dec 17, 2024
This isn't a TS bug. Your callback isn't inferred as returning a type predicate as implemented in #57465, because it would need to be a one-sided type predicate, which TypeScript doesn't have. The check
shape.type === 'Square'can narrowRound | SharptoSharpbut sinceshape.type !== 'Square'does not narrowRound | SharptoRound, no type predicate can be inferred. (SeeflakyIsStringin the text of #57465) If TS had one-sided type predicates as requested in #15048, then the return type could be inferred as something likeshape is Shape else falseand it would work. When you're usingfind()you don't care about the false side of the type guard function, but TS does, and there's currently no way to tell it not to.If you want, you can annotate your callback with a type predicate return type, and while
shape is Sharpwill work, it's a bit of a lie. Something which works and is true is:Playground link to code
elusiveunit commentedon Dec 18, 2024
I see, thanks for the explanation! We'll make do with some helpers then and keep an eye on the one-sided predicate issue.