microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.99k stars 12.48k forks source link

Supply label names in tuple types using type-level strings #56093

Open Diggsey opened 1 year ago

Diggsey commented 1 year ago

🔍 Search Terms

Labelled tuples

✅ Viability Checklist

⭐ Suggestion

It's already possible to manually specify labels when defining a tuple type:

type MyTuple = [foo: number, bar: string];

I propose that there be some syntax to allow these labels to be supplied explicitly in mapped types, eg.

type Labels = ["foo", "bar"]
type Values = [number, string]
type MyTuple = {
    // Hypothetical syntax
    [I in keyof Values]: Values[I] as Labels[I]
}

To be clear: there is still no way to "access" labels of tuples under this proposal. It's purely a way to add extra documentation into existing type definitions.

If the provided name can be determined to be a literal string type, then the value of that literal string is used as the tuple element name. In all other cases, the element is unnamed as before.

📃 Motivating Example

There are many cases where the developer can supply useful names, such as if the tuple is somehow derived from an object type, or similar.

My particular case involves taking a user-supplied array like the following:

const path = ["examples", param("exampleId"), "line", param<number>("lineNumber")] as const;

And I use this constant to generate a type for paths, which in this case would look like:

type Path = TypeMagic<typeof path>;
// type Path = ["examples", string, "line", number]

It would be nicer if I could generate this type instead:

type Path = TypeMagic<typeof path>;
// type Path = ["examples", exampleId: string, "line", lineNumber: number]

💻 Use Cases

This came up while integrating TanStack Query into an application.

Since this is purely a documentation improvement, the workaround for the moment is to simply do without labels.

RyanCavanaugh commented 1 year ago

I just wanted to say thank you for having a tuple label feature request that actually follows the spirit of tuple labels 🥰

jcalz commented 1 year ago

Cross-linking to #44939.

Personally I think it would be reasonable to also access labels as string literal types so you can transform labels (e.g., from [age: number] to [ageDefault: number]), as long as labels still have no effect on the assignability of the tuples themselves, and possibly even only have them readable from within a label-writing expression:

// Not wedded to this

// overwrite label if appearing in a tuple
type WithLabel<L extends string, T> = intrinsic;

// read tuple labels as string literal types, 
// but only within the L part of a WithLabel<L, T> expression
type Labels<T extends any[]> = intrinsic;

type A = [WithLabel<"abc", 123>, WithLabel<"def", 456>];
// type A = [abc: 123, def: 456];

type B = [WithLabel<Labels<A>[0], 789>];
// type B = [def: 789]; 

type Nope = Labels<A>;
// type Nope = [string, string]; // unobservable here

type DefaultLabels<T extends any[]> = { [I in keyof T]:
    WithLabel<`${Labels<T>[I]}Default`, T[I]> }

type C = DefaultLabels<[ghi: string, jkl: number, mno: boolean]>
// type C = [ghiDefault: string, jklDefault: number, mnoDefault: boolean]
Andarist commented 10 months ago

After reading this, I feel like the original proposal is not that different from my experiments in https://github.com/microsoft/TypeScript/pull/55452 . It just uses a different syntax - but the overall goals are the same: to provide extra documentation through dynamic labels without impacting assignability.

tmm commented 9 months ago

Would love a feature like this! Shared a motivating example on the Design Meeting Notes for the experiment @Andarist is mentioning. https://github.com/microsoft/TypeScript/issues/55511#issuecomment-1747675356

Yuripetusko commented 3 months ago

We also would greatly benefit from named/labeled tuples. Very useful when converting JSON ABI (which has option to provide more context to array elements, including names) to typescript (as per @tmm example). Very prone to bugs without this additional metadata for tuple elements