Anthony Lau
Anthony Lau

Reputation: 17

Typescript 3.5.3 Why object type `{ sub: 123 }` infer as string?

Typescript 3.5.3

{ sub: 123 } infer as String when pass to a function with param type string | object.

Example

function foo(bar: string | object) {
    return 'test';
}

foo({ sub: 123 });

Error Message

error TS2326: Types of property 'sub' are incompatible.
Types of property 'sub' are incompatible.
  Type 'number' is not assignable to type '(() => string) | undefined'.

I expect it infer as object.

Upvotes: 0

Views: 77

Answers (1)

jcalz
jcalz

Reputation: 329773

This is a known bug in TypeScript 3.5 which was introduced along with (generally) improved support for excess property checking in union types.

The problem here is that string values have some deprecated HTML wrapper methods that output a version of the string surrounded by some HTML tags. One of them is sub. Watch this craziness:

console.log("wat".sub()); // <sub>wat</sub>

The new excess property checking in TS3.5 looked at string | object and the value {sub: 123} and said, "well, string has a sub property of type ()=>string, and object doesn't have a known sub property, so we will enforce excess property checking here by making object's sub property only accept undefined. That means we need sub to be of type (()=>string) | undefined, and 123 doesn't match that. ERROR!"

For now, you will have to work around this with some sort of type widening or assertion:

foo({ toFixed: 123 } as object); // workaround in TS3.5

Luckily, it looks like this bug will be fixed in TypeScript 3.6. The fix is, I think, to simply ignore primitive types like string in unions when doing excess property checking.

foo({ sub: 123 }); // error in TS3.5, okay in TS3.6

So your code will start working again. You can expect the following to continue to fail, though:

function baz(bar: { sub: () => string } | object) {
  return 'test';
}

baz({ sub: 123 }); // error in TS3.5 and TS3.6

Okay; hope that helps. Good luck!

Link to code

Upvotes: 1

Related Questions