microsoft/TypeScript

type deduce error

Closed this issue ยท 7 comments

๐Ÿ”Ž Search Terms

type deduce

๐Ÿ•— Version & Regression Information

all version

โฏ Playground Link

No response

๐Ÿ’ป Code

const num = (): number => {
    return 10;
};

const numAndObj = (arg: number) => {
    if (arg) {
        return arg;
    } else {
        return {
            name: "test",
            age: 10,
        };
    }
};

const numOrObj = num() ?? numAndObj(10);
const numOrObj2 = num() || numAndObj(10);

const test = (num: number) => {};

test(numOrObj);
test(numOrObj2);

๐Ÿ™ Actual behavior

tsc test.ts

test.ts:22:6 - error TS2345: Argument of type 'number | { name: string; age: number; }' is not assignable to parameter of type 'number'.
Type '{ name: string; age: number; }' is not assignable to type 'number'.

22 test(numOrObj2);
~~~~~~~~~

Found 1 error in test.ts:22

๐Ÿ™‚ Expected behavior

tsc --noEmit test.ts
test.ts:21:6 - error TS2345: Argument of type 'number | { name: string; age: number; }' is not assignable to parameter of type 'number'.
Type '{ name: string; age: number; }' is not assignable to type 'number'.

21 test(numOrObj);
~~~~~~~~

test.ts:22:6 - error TS2345: Argument of type 'number | { name: string; age: number; }' is not assignable to parameter of type 'number'.
Type '{ name: string; age: number; }' is not assignable to type 'number'.

22 test(numOrObj2);
~~~~~~~~~

Found 2 errors in the same file, starting at: test.ts:21

Additional information about the issue

type of numOrObj is deduce error

in vscode:

Image Image

This is working as intended.

The type of numOrObj is number, because the coalesce operator ?? only uses the right-hand expression when the left-hand one is null or undefined, but according to the return type definition of num() this can never be the case.

But with numOrObj you use the logical-or operator ||, which uses the right-hand expression when the left-hand one is falsy. The return type definition says it can return number, and the number 0 is falsy, so the right-hand expression might be used. At this point the compiler doesn't know that according to the implementation of num() the return value is always truthy. For this to work the compiler would need to be able to declare the return type as "number except 0", but this would require "Negated Types" (#4196) first.

when use tsc to check test.ts file, that got a error:

tsc --noEmit test.ts
test.ts:21:6 - error TS2345: Argument of type 'number | { name: string; age: number; }' is not assignable to parameter of type 'number'.
Type '{ name: string; age: number; }' is not assignable to type 'number'.

21 test(numOrObj);



> This is working as intended.
> 
> The type of `numOrObj` is `number`, because the coalesce operator `??` only uses the right-hand expression when the left-hand one is `null` or `undefined`, but according to the return type definition of `num()` this can **never** be the case.
> 
> But with `numOrObj` you use the logical-or operator `||`, which uses the right-hand expression when the left-hand one is _falsy_. The return type definition says it can return `number`, and the number `0` is falsy, so the right-hand expression **might** be used. At this point the compiler doesn't know that according to the implementation of `num()` the return value is **always** truthy. For this to work the compiler would need to be able to declare the return type as "number except 0", but this would require "Negated Types" ([#4196](https://github.com/microsoft/TypeScript/issues/4196)) first.

Yes, that error is correct.

๐Ÿค– This is an automated response. I looked for things that might help (duplicates, FAQ entries, etc) but didn't find anything. A human will take a look!

Yes, that error is correct.

I think that is incorrect, because when use:

// not specify file
tsc --noEmit
test.ts:22:6 - error TS2345: Argument of type 'number | { name: string; age: number; }' is not assignable to parameter of type 'number'.
Type '{ name: string; age: number; }' is not assignable to type 'number'.

22 test(numOrObj2);

// specify file:
tsc --noEmit test.ts
test.ts:21:6 - error TS2345: Argument of type 'number | { name: string; age: number; }' is not assignable to parameter of type 'number'.
Type '{ name: string; age: number; }' is not assignable to type 'number'.

21 test(numOrObj);


test.ts:22:6 - error TS2345: Argument of type 'number | { name: string; age: number; }' is not assignable to parameter of type 'number'.
Type '{ name: string; age: number; }' is not assignable to type 'number'.

22 test(numOrObj2);

Found 2 errors in the same file, starting at: test.ts:21

for the same file, different results.

and when compile test.ts:
tsc test.ts
test.ts:21:6 - error TS2345: Argument of type 'number | { name: string; age: number; }' is not assignable to parameter of type 'number'.
Type '{ name: string; age: number; }' is not assignable to type 'number'.

21 test(numOrObj);
~~~~~~~~

test.ts:22:6 - error TS2345: Argument of type 'number | { name: string; age: number; }' is not assignable to parameter of type 'number'.
Type '{ name: string; age: number; }' is not assignable to type 'number'.

22 test(numOrObj2);
~~~~~~~~~

Found 2 errors in the same file, starting at: test.ts:21

Image

the numOrObj type is 'number | {name:string, age: number}', is not a number

When you specify the files then your local tsproject.json is not used, so you likely compile with different settings (e.g. strict is turned off by default).

When you specify the files then your local tsproject.json is not used, so you likely compile with different settings (e.g. strict is turned off by default).

thanks very much for you answer