Skip to content

False-positive errors regarding string array const #52684

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
mocanew opened this issue Feb 8, 2023 · 3 comments · Fixed by #52769
Closed

False-positive errors regarding string array const #52684

mocanew opened this issue Feb 8, 2023 · 3 comments · Fixed by #52769
Assignees
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue

Comments

@mocanew
Copy link

mocanew commented Feb 8, 2023

Bug Report

🕗 Version & Regression Information

It's not a regression, the same behaviour can be observed on TS 4 and 5. (for TS 4, using ts-toolbelt's Narrow function gives equivalent results)

💻 Code

const staticPath1Level = ["home"] as const;
const staticPath2Level = ["home", "user"] as const;
const staticPath3Level = ["home", "user", "downloads"] as const;

const randomID = 'id' as string;

// TS 5
declare function foo<const T extends string[] | readonly string[]>(path: T): T;
// TS 4
// declare function foo<T extends string[] | readonly string[]>(path: F.Narrow<T>): T;

const a1 = foo([...staticPath1Level, randomID, 'doc.pdf']);
const a2 = foo([...staticPath2Level, randomID, 'doc.pdf']);
const a3 = foo([...staticPath3Level, randomID, 'doc.pdf']);

const b1 = foo([...staticPath1Level, randomID, 'folder', 'doc.pdf']);
const b2 = foo([...staticPath2Level, randomID, 'folder', 'doc.pdf']);
const b3 = foo([...staticPath3Level, randomID, 'folder', 'doc.pdf']);

const c1 = foo([...staticPath1Level, randomID, 'folder', 'subfolder', 'doc.pdf']);
const c2 = foo([...staticPath2Level, randomID, 'folder', 'subfolder', 'doc.pdf']);
const c3 = foo([...staticPath3Level, randomID, 'folder', 'subfolder', 'doc.pdf']);

const d1 = foo([...staticPath1Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf']);
const d2 = foo([...staticPath2Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf']);
const d3 = foo([...staticPath3Level, randomID, 'folder', 'subfolder', 'another-subfolder', 'doc.pdf']);

🙁 Actual behavior

All return types are correct, e.g.:

const b3: readonly ["home", "user", "downloads", string, "folder", "doc.pdf"]

The call for a2 has the following error (b3, c3 also have the same error type, adjusted for their parameters):

ts(2345)
Argument of type '["home", "user", string, string]' is not assignable to parameter of type 'readonly ["home", "user", string, "doc.pdf"]'.
Type at position 3 in source is not compatible with type at position 3 in target.
Type 'string' is not assignable to type '"doc.pdf"'.

The call for b2 has the following error (c2, d2, d3 also have the same error type, adjusted for their parameters):

ts(2322)
Type 'string' is not assignable to type '"folder"'.

🙂 Expected behavior

Don't generate false-positive errors. Also, it's odd that there is no apparent pattern to when one of the errors is shown.

Workaround

To silence these errors, as const can be used, but again, it's not really obvious where or how many times.
This partial code sample generates no errors:

const b2 = foo([...staticPath2Level, randomID, 'folder' as const, 'doc.pdf']);
const b3 = foo([...staticPath3Level, randomID, 'folder', 'doc.pdf' as const]);

This partial code sample still has false-positive errors:

const b2 = foo([...staticPath2Level, randomID, 'folder', 'doc.pdf' as const]);
const b3 = foo([...staticPath3Level, randomID, 'folder' as const, 'doc.pdf']);
@mocanew
Copy link
Author

mocanew commented Feb 8, 2023

It's not really related to const modifier, but maybe @ahejlsberg might have some relevant context regarding this.

@ahejlsberg
Copy link
Member

Yeah, there's an issue here relating to how we compute contextual types for array literal elements in positions following a spread element. We wrongly assume that the position of the element in the array literal corresponds to the index of the element in the resulting tuple type--which isn't always the case when there's a preceding spread element.

@ahejlsberg ahejlsberg added the Bug A bug in TypeScript label Feb 10, 2023
@ahejlsberg ahejlsberg self-assigned this Feb 10, 2023
@ahejlsberg ahejlsberg added this to the TypeScript 5.0.1 milestone Feb 10, 2023
@ahejlsberg
Copy link
Member

Simpler example that reproduces with 5.0 as well as earlier versions:

declare function foo<T extends [number, number, number, 'a' | 'b']>(x: T): T;

const c3 = [1, 2, 3] as const;

foo([1, 2, 3, "a"]);
foo([...c3, "a"]);  // Unexpected error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants