TypeScript icon indicating copy to clipboard operation
TypeScript copied to clipboard

[Control flow] Array filter with typeguard works differently following array initial value

Open paulsouche opened this issue 1 year ago • 1 comments

🔎 Search Terms

filter + typeguard + control flow + strictNullChecks

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about filter + typeguards + control flow + strictNullChecks

⏯ Playground Link

https://www.typescriptlang.org/play/?strict=false&noImplicitAny=false&strictFunctionTypes=false&strictPropertyInitialization=false&strictBindCallApply=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&esModuleInterop=false&declaration=false&target=0&jsx=0&module=0&ts=5.6.2&experimentalDecorators=false&emitDecoratorMetadata=false#code/MYewdgzgLgBATgQwO4C4bTgSzAcwNoC6MAPjAIJyICeAPBtjiTGAK4A2bAfDALwyEBuAFBDQkWADNMbKAFM4sgCZp6uQr3jIAdFJnyAFPuiyADiqhZcTVhwCU50zEwR0Fhr25QqJ2SAmvHHiCYAHJVHBDbGAB6aJgAaQB5ETFoV0scACVkcwz1UgpqOjcrUhsuDTwAIhM2EBMqgmFRcDTwgDFpOQVldIZ1PnDspB0ugyM5Mz7S5nY2ewCTJxdwjxgvHz9F3mCwkoio2ITklvFZjmHc-qICygRaVbK57j48cqaU1thyzr0eq7URD45WGoz+hmMU0e53mDiWzmmjB4nm8vn8kJ2fD2GUiMTiiXinzO5QACmwWBAAMr7S6I-LkO4PfbWZ6VGp1BoAGhhH1OaVJ5Kp+1+3SUAPwQJhZIp1IyoN03Qhk3FLLscOWiLWGzR2yCWPCuKOBKEQA

💻 Code

const raw: string[] | (string | null)[] = [];

const filtered: string[] = raw.filter((step: string | null): step is string => typeof step === 'string') // KO
// Type '(string | null)[]' is not assignable to type 'string[]'.
const raw: string[] |(string | null)[] = ["plop", null];

const filtered: string[] = raw.filter((step: string | null): step is string => typeof step === 'string') // OK no error

🙁 Actual behavior

There is a strange difference in control flow when you filter nullish values in an array regarding the declaration of that array. This is definitely a edge case. The type string[] | (string | null)[] comes from an union type in my case.

🙂 Expected behavior

No difference and typeguard should work (regardless the fact it is valid).

Additional information about the issue

I first thought it was a regression due to #57465 but I tested it on several version. The issue has always been here. I looked into a lot of issues about typeguards and filters but unfortunately, could not find one similar to my issue :cry:

Thanks anyway for the great job

paulsouche avatar Sep 30 '24 17:09 paulsouche

It feels like a duplicate of https://github.com/microsoft/TypeScript/issues/60006 . However, the experiment from https://github.com/microsoft/TypeScript/pull/60036 doesn't help this case.

When combining union signatures with type parameters those have to be identical. They are not the same here because they have different constraints (S extends string vs S extends string | null). So those signatures you'd like to be used here are discarded within getUnionSignatures and only the non-filtering ones are left as viable ones.

Andarist avatar Oct 01 '24 08:10 Andarist