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 guards strangeness #7224

Closed
zpdDG4gta8XKpMCd opened this issue Feb 24, 2016 · 6 comments
Closed

type guards strangeness #7224

zpdDG4gta8XKpMCd opened this issue Feb 24, 2016 · 6 comments

Comments

@zpdDG4gta8XKpMCd
Copy link

TypeScript Version:

1.8.0

Code

const enum AsNonBlank {}
export function isNonBlank(value: string) : value is string & AsNonBlank {
    return !/^\s*$/g.test(value);
}
let value: string;
if (isNonBlank(value)) {
    doThis(value); // expected value to be string & AsNonBlank, actual: string
} else {
    doThat(value);
}
function doThis(value: string & AsNonBlank): void {
}
function doThat(value: string) : void {
}

Expected behavior:
value should either be string & AsNonBlank
or there must be a message saying that the type guard is invalid or something
Actual behavior:
value is a string

@yortus
Copy link
Contributor

yortus commented Feb 25, 2016

@Aleksey-Bykov I'm also finding that user-defined type guards often don't work the way I intuitively expect. Your example makes intuitive sense to me and I would have expected it to work.

However, since you haven't started with a union type, I suspect you are working outside the bounds of what tsc can currently do with type guards. I got your example to work by changing the declaration of value to:

let value: string | (string & AsNonBlank);

This seems to satisfy the compiler, and the narrowing happens as expected in both the if and else branches.

@yortus
Copy link
Contributor

yortus commented Feb 25, 2016

Interestingly, the following version of your code does work as you expect:

const enum AsUnaryFunc {}
export function isUnaryFunc(value: Function) : value is Function & AsUnaryFunc {
    return value.length === 1;
}
let value: Function;
if (isUnaryFunc(value)) {
    doThis(value); // expected value to be Function & AsUnaryFunc, AND IT IS
} else {
    doThat(value);
}
function doThis(value: Function & AsUnaryFunc): void {
}
function doThat(value: Function) : void {
}

It's the same code as yours, just with Function/AsUnaryFunction instead of string/AsNonBlank.

@zpdDG4gta8XKpMCd
Copy link
Author

@yortus thanks!

@zpdDG4gta8XKpMCd
Copy link
Author

@yortus just checked with the union, it doesn't work either

const enum AsNonBlank {}
export function isNonBlank(value: string | string & AsNonBlank) : value is string & AsNonBlank {
    return !/^\s*$/g.test(value);
}
let value: string;
if (isNonBlank(value)) {
    doThis(value); // expected value to be string & AsNonBlank, actual is *STILL* a string
} else {
    doThat(value);
}
function doThis(value: string & AsNonBlank): void {
}
function doThat(value: string) : void {
}

@yortus
Copy link
Contributor

yortus commented Feb 26, 2016

Sorry I wasn't clear - this one works for me:

const enum AsNonBlank {}
export function isNonBlank(value: string) : value is string & AsNonBlank {
    return !/^\s*$/g.test(value);
}
let value: string | (string & AsNonBlank);    // <-- union here
if (isNonBlank(value)) {
    doThis(value); // expected value to be string & AsNonBlank
} else {
    doThat(value);
}
function doThis(value: string & AsNonBlank): void {
}
function doThat(value: string) : void {
}

@zpdDG4gta8XKpMCd
Copy link
Author

Thanks! That's a great help!

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants