镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

type guards strangeness #7224

Description

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

Activity

  1. yortus commented on Feb 25, 2016

    @yortus
    Contributor

    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.

  2. yortus commented on Feb 25, 2016

    @yortus
    Contributor

    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.

  3. zpdDG4gta8XKpMCd commented on Feb 25, 2016

    @zpdDG4gta8XKpMCd
    Author
  4. zpdDG4gta8XKpMCd commented on Feb 26, 2016

    @zpdDG4gta8XKpMCd
    Author

    Troy Gerwien (@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 {
    }
  5. yortus commented on Feb 26, 2016

    @yortus
    Contributor

    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 {
    }
  6. added a commit that references this issue on Jun 2, 2016
    ef0f6c8
  7. zpdDG4gta8XKpMCd commented on Jun 2, 2016

    @zpdDG4gta8XKpMCd
    Author

    Thanks! That's a great help!

  8. locked and limited conversation to collaborators on Jun 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions