Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type inference fails with immediate type guards #18562

Closed
NaridaL opened this issue Sep 18, 2017 · 6 comments
Closed

Type inference fails with immediate type guards #18562

NaridaL opened this issue Sep 18, 2017 · 6 comments
Labels
Duplicate An existing issue was already created

Comments

@NaridaL
Copy link
Contributor

NaridaL commented Sep 18, 2017

TypeScript Version: master / 2.6.0-dev20170914 / whatever the playground has currently
I searched for immediate type guard and type guard inference , I hope I didn't miss an existing issue.

The issue is that the type inference fails for overloads with type guards such as the one for Array.filter when the type guard is passed as an immediate value. See also #7657 The problem seems to be that TS infers the type of the argument e of the type guard from the type of the callback parameter of filter. In doing so, the return type of type guard is lost. If you specify the argument e of the type guard explicitly, it works as expected.

Code

// for ref the Array signature.
interface Array<T> {
    filter<S extends T>(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[];
}

declare const arr: (string | number)[]

// Property 'substring' does not exist on type 'string | number'.
arr.filter((e): e is string => 'string' == typeof e)[0].substring(0, 10);

arr.filter((e: any): e is string => 'string' == typeof e)[0].substring(0, 10); // ok

const isStringArrow = (e: any): e is string => 'string' == typeof e;
arr.filter(isStringArrow)[0].substring(0, 10); // ok

// Property 'substring' does not exist on type 'string | number'.
arr.filter(function (e): e is string { return 'string' == typeof e })[0].substring(0, 10);

// Property 'substring' does not exist on type 'string | number'. 
arr.filter(function (e: any): e is string { return 'string' == typeof e })[0]
    .substring(0, 10);

arr.filter(function (this: void, e: any): e is string { return 'string' == typeof e })[0]
    .substring(0, 10); // ok

const isString = function (e: any): e is string { return 'string' == typeof e }; 
arr.filter(isString)[0].substring(0, 10); // ok

playground

Expected behavior:
All cases should work.
Actual behavior:
See above.

@ghost
Copy link

ghost commented Oct 20, 2017

Should wait on #17600 before working on this.

@dgreene1
Copy link

dgreene1 commented Feb 27, 2018

Should wait on #17600 before working on this.

Since #17600 got merged, can we pick this ticket up again?

Please let me know how I could help.

Example of the workaround, which does not scale well:

// Still resolves to (string | undefined)[]
var result = ["hello", undefined].filter(x => x != null);

// I had to work harder, but I eventually got TypeScript to believe that it's a string[]
function isString(test: any): test is string{
    return typeof(test) === "string";
}
var annoyinglyAchievedResult = ["hello", undefined].filter(isString);

Many of us are just used to writing this like !!x

P.S. As always, thank you so much for the wonderful work you're all doing. :)

@emilio-martinez
Copy link

@andy-ms can this be picked up again? Filter functions under strictNullChecks are a pain to workaround at this point

@ghost
Copy link

ghost commented May 2, 2018

Related: #10734

Note that all examples in the original post are working now. The second comment is about inferring a function to be a type guard without an explicit annotation. E.g., x => !!x is currently => boolean but might be inferred as => x is not null (but that would require negation types (#4196)?)

@mhegazy
Copy link
Contributor

mhegazy commented May 2, 2018

The examples in the OP should all now be working with #5101 fixed.

The examples in #18562 (comment) require the type guards to be automatically inferred, which is tracked by #16069

@mhegazy mhegazy added the Duplicate An existing issue was already created label May 2, 2018
@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@microsoft microsoft locked and limited conversation to collaborators Jul 31, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

5 participants