Skip to content

as const does not always narrow array literals to tuple typesΒ #57727

Closed as not planned

Description

@jflatow

πŸ”Ž Search Terms

as const, narrow, widen, tuple

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about as const narrowing.

⏯ Playground Link

https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgCoGVkG8BQzkgQDuyAFAHSVxQDmAzgFzJwgCeA2gLoCUTLrAbhwBfHDlCRYiFAEF0AHgAKyCAA9IIACZ1ki9AD5seZOzhM6YKKBqcmAJQhxNAexAAbVjKhQ4reQGsIVmcYXX0hUXFwaHgkXUxcfHYAB3NLa1s0dAixMFZk2Vo6eVQVdQgtHQcnVw8vHz9A4NDFfQAaXTKNbXjDAF40Loqe9lAYaGQAMWAoCw7KcjGJhwtOYwB+E2nZsCHK5CaQzs2ASRALFiRUfIgldm3VwyZCADdoecovenkV3bVuqqOFzuTzeXwBIJHVrITa-ZBMLgdVprfAIzhCMQSGLSZB2eQyPY9ORKdqdf7DHR6QyJZAweQAEUJOkOoRk+lImiY9I6ZmQX2KMnY9M4SP0vGQL2cwE0ORwCFcFmQqmQAxpACMEQByACOms4zB07B1eo6AHpTSpvM4oMgiMAwAALZwAV12cENxpRyAQWt1IpEBu9CrAQnl512rBV2GQ2qYADlnQBbNUTYSBsMWDEwZ0gBBgYCuZCQCykKD2eR5ApHVQdSsQI6sMVGfBQcgwUiatWajrsACsPByQA

πŸ’» Code

interface TS {
  new (...args: any[]): any;
}

interface AS<P extends PS> {
  [a: string]: ReadonlyArray<keyof P>;
}

interface PS {
  [p: string]: TS;
}

type Args<T extends ReadonlyArray<keyof P>, P extends PS> = T extends [infer First, ...infer Rest]
  ? [First extends keyof P ? InstanceType<P[First]> : never, ...Args<Rest extends ReadonlyArray<keyof P> ? Rest : [], P>]
  : [];


interface R<A extends AS<P>, P extends PS> {
  f<D extends keyof A>(d: D, a: Args<A[D], P>): void;
}

const x = {
  b: ['q'] as ['q'], // error without as ['q']
  c: ['q'],
} as const;
const y = { q: Number } as const;

function test(r: R<typeof x, typeof y>) {
  r.f('b', [5]);
}

πŸ™ Actual behavior

Without the as ['q'] cast (see comment in code), the code fails to compile, presumably due to not correctly inferring the narrow tuple type and inferring an array type instead.

πŸ™‚ Expected behavior

When using the as const assertion on an object literal, TypeScript is expected to infer the most specific literal types for all properties, including narrowing array literals to tuple types. However, in the context of a mapped type and conditional type, this behavior seems to be inconsistent, leading to a type mismatch error.

Additional information about the issue

The issue occurs in the context of mapped types (AS<P>) and conditional types (Args<T, P>). The as const assertion is expected to provide the most specific literal types, but it fails to narrow the array literal to a tuple type automatically, leading to the type mismatch error.

Possible Solution:
Ensure that the as const assertion consistently narrows array literals to tuple types within object literals, even in the context of mapped types and conditional types, aligning with the expected behavior described in the TypeScript documentation.

Activity

  1. Andarist commented on Mar 11, 2024

    @Andarist
    Contributor

    I didn't analyze what you are trying to achieve here but I was able to fix ur problem by adding readonly to your inference target in Args: TS playground

  2. RyanCavanaugh commented on Mar 11, 2024

    @RyanCavanaugh
    Member

    I don't see a defect demonstrated here. The additional as changes the type of the expression and as a result, type computations change.

    It's the same as the difference between these two declarations:

    /* explicitly annotated */
    declare const x: { readonly b: readonly ['q'], readonly c: readonly ['q'] }

    vs

    /* inferred type from additional 'as' */
    declare const x: { readonly b: ['q'], readonly c: readonly ['q'] }
  3. jflatow commented on Mar 12, 2024

    @jflatow
    Author

    I didn't analyze what you are trying to achieve here but I was able to fix ur problem by adding readonly to your inference target in Args: TS playground

    Thanks, that indeed fixes the error.

  4. jflatow commented on Mar 12, 2024

    @jflatow
    Author

    I don't see a defect demonstrated here. The additional as changes the type of the expression and as a result, type computations change.

    Perhaps, but the change in type computations is at least odd to me. By adding the as ['q'] it does indeed seem to change from interpreting as a tuple to an array in the given context. But the additional readonly seems to prevent that. I'm confused why this is happening without it but glad it makes sense to you.

  5. typescript-bot commented on Mar 15, 2024

    @typescript-bot
    Contributor

    This issue has been marked as "Not a Defect" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

  6. locked as resolved and limited conversation to collaborators on Oct 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Not a DefectThis behavior is one of several equally-correct options

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions