Skip to content

Tag template of arrow function infers wrong type #36700

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
danieldietrich opened this issue Feb 8, 2020 · 8 comments
Closed

Tag template of arrow function infers wrong type #36700

danieldietrich opened this issue Feb 8, 2020 · 8 comments

Comments

@danieldietrich
Copy link

TypeScript Version: 3.8.0-dev.20200208

Search Terms: tag function, tagged template, template string

Code

{
    function tag(strings: TemplateStringsArray, ...args: unknown[]): [TemplateStringsArray, unknown[]] {
        return [strings, args];
    }

    // ✅ inferred type: [TemplateStringsArray, unknown[]]
    const t = tag`123`;

    // console.log(t[2]); // it is correct that this would not compile
}

{
    const tag = (strings: TemplateStringsArray, ...args: unknown[]) => [strings, args];

    // 🛑 inferred type: (TemplateStringsArray | unknown[])[]
    const t = tag`123`;

    console.log(t[2]); // should not compile, but it does!
}

Expected behavior:

The console.log(t[2]) statement should produce the following error:

Tuple type '[TemplateStringsArray, unknown[]]' of length '2' has no element at index '2'. ts(2493)

Actual behavior:

The example does compile (but it shouldn't).

Playground Link: here

Related Issues: []

@Zemnmez
Copy link

Zemnmez commented Feb 8, 2020

It looks like this type inferrence is at least consistent with calling the same function without using template syntax:

{
    function tag(strings: TemplateStringsArray, ...args: unknown[]): [TemplateStringsArray, unknown[]] {
        return [strings, args];
    }

    // ✅ inferred type: [TemplateStringsArray, unknown[]]
    const t = tag`123`;

    // console.log(t[2]); // it is correct that this would not compile
}

{
    const tag = (strings: TemplateStringsArray, ...args: unknown[]) => [strings, args];

    // 🛑 inferred type: (TemplateStringsArray | unknown[])[]
    const t = tag`123`;

    console.log(t[2]); // should not compile, but it does!
}

{
    const tag = (strings: TemplateStringsArray, ...args: unknown[]) => [strings, args];

    // 🛑 inferred type: (TemplateStringsArray | unknown[])[]
    const t = tag(Object.assign(["123"], {raw: ["123"]}));

    console.log(t[2]); // should not compile, but it does!
}

Playground Link

@danieldietrich
Copy link
Author

That's right.

An additional type witness does solve the problem. But it should work without it.

{
    const tag = (strings: TemplateStringsArray, ...args: unknown[]) => [strings, args];

    // 🛑 inferred type: (TemplateStringsArray | unknown[])[]
    const t = tag`123`;

    console.log(t[2]); // should not compile, but it does!
}

{
    const tag = (strings: TemplateStringsArray, ...args: unknown[]): [TemplateStringsArray, unknown[]] => [strings, args];

    // 🛑 inferred type: (TemplateStringsArray | unknown[])[]
    const t = tag`123`;

    console.log(t[2]); // it is correct that this does not compile
}

Playground Link

@Zemnmez
Copy link

Zemnmez commented Feb 8, 2020

I think this is down to typescript not inferring tuple return types at all, and therefore not being able to correctly intuit the response here. I think this is a feature request, rather than a bug then.

Consider this minimal example:

{
    // f: () => number[], rather than f: () => [1,2]
    const f = () => {
        return [1,2]
    }
}

Playground Link

@danieldietrich
Copy link
Author

danieldietrich commented Feb 8, 2020

You are absolutely right. Without an additional type hint, type inference for a plain function behaves exactly the same. I will close this issue (and I will not create a feature request).

Many thanks for your investigation!

{
    // f: () => number[], rather than f: () => [1,2]
    const f = () => {
        return [1,2]
    }
}

{
    // f(): number[], perfectly consistent
    function f() {
        return [1,2]
    }
}

Playground Link

@Zemnmez
Copy link

Zemnmez commented Feb 8, 2020

np! I would love to see this inference too :)

@danieldietrich
Copy link
Author

I think it would be risky, the change might not be source compatible regarding existing code bases. But currently I can't find a scenario where it would break existing code.

function f(ns: number[]) { }

const arr: [1, 2] = [1, 2]

f(arr); // works!

Playground Link

@Zemnmez
Copy link

Zemnmez commented Feb 8, 2020

looks like this is issue #25516

@danieldietrich
Copy link
Author

Yes, it is. I would love to see more precise type inference.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants