Skip to content

Assertion condition conflicts with know type #37493

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

Closed
wmaca opened this issue Mar 20, 2020 · 3 comments
Closed

Assertion condition conflicts with know type #37493

wmaca opened this issue Mar 20, 2020 · 3 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@wmaca
Copy link

wmaca commented Mar 20, 2020

TypeScript Version: 3.9.0--dev.20200319

Search Terms: assertion condition, narrow assertion condition, assertion condition union

Code

type MyUnion = { [key: string]: any } | string[];

function assertObject(value: any): asserts value is Record<string, any> {
    if (value === null || typeof value !== "object" || Array.isArray(value)) {
        throw new Error("Assertion failed");
    }
}

function getValue(): MyUnion {
    return { foo: "bar" };
}

const value = getValue();

assertObject(value);

// Error as it does not know if it is a string[] or { [key: string]: any }
console.log(value["propName"]); 

I've also observed that TypeScript is using the already known type at caller (i.e. MyType) to narrow after the assert function is called. This is resulting in never as a result.

I am not sure this part is behaving as expected, but seems to be related to the bug reported.

type MyType =
	| {
			[key: string]: {
				[k: string]: number;
			};
	  }
	| string;

const a: MyType = { foo: { bar: 123 } };

function assertUndefined(value: any): asserts value is undefined {
	if (value !== undefined) throw new Error();
}

assertUndefined(a.foo);

// `a.foo` is `never`
console.log(a.foo);

Expected behavior: assert condition should narrow the type to one of the values in the union.

Actual behavior: it seems to be merging what is knows about the type at the caller with what the assertion returns.

Playground Link: https://www.typescriptlang.org/v2/en/play?ts=next#code/C4TwDgpgBAsiCqA7Alge0VAvFA3lA2gNYQgBcUAzsAE7KIDmAuuQIaIhQC+UAPpTXXr5GAbgBQYgGYBXRAGNgaDCwoUI1YAHkARgCsICgBQA3FgBtpEVuwCUrVeuAUopi9GTOASgdTUAJgA8VLQMADRQbCAAfLhiUPFQyJJQJuaWWJjYiNJmZrx8oJCoya7pAISZUABEqHoGwFX5UACC1NQsIAB0Hq3tIKluNjaxCaNQwAAW1KgA7lCIEHMAom2+hlXNDhpKUJIsyGYQflU24qOcYhdSsgo79BDAAGppEIZ2sAgo6CMJ1A-S1AweEkqFQ5Cq2hY1EanHEVzk6CoLheWCg9yeLze4jEKjUGh0+iMpQgpwkAHoyVAVtNqBFnMhgFA-KgIM5EKhGYR2XMkolGR4IvwQkJGFBfLgCMQyELBMwIuwuGIEYgKKhDp0zKh6ANLPgqmBpmAAHIsAC2ECqjFOUCAA

Related Issues: #37241

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Mar 20, 2020
@RyanCavanaugh
Copy link
Member

string[] is a subtype of Record<string, any>, so the narrowing condition does nothing.

You could write something like this to indicate that you're excluding arrays from the result:

function assertObject(value: any): asserts value is { push?: never; [key: string]: any }```

@wmaca
Copy link
Author

wmaca commented Mar 20, 2020

Thanks, @RyanCavanaugh, for your answer and your time. I was suspecting this could be the case, but I did the lazy thing and brought the issue here. Nevertheless, now you can't have the key push, right?

But how about the second case I've added? Does TypeScript strictly follow the assertion or it merges with the type information it has on the caller? I am not sure this is my fault, but I don't recall finding deeper details on the documentation.

@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants