microsoft / TypeScript

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

Smarter type alias preservation for unions originating from type query types #47828

Open Gerrit0 opened 2 years ago

Gerrit0 commented 2 years ago

Suggestion

πŸ” Search Terms

smarter type alias preservation, type query types

βœ… Viability Checklist

My suggestion meets these guidelines:

⭐ Suggestion

TypeScript 4.2 introduced intelligent type alias preservation for most union types. This greatly improved visibility when dealing with large union types. This preservation should be extended to union types created by via type query types.

Could probably also be considered a bug / missing edge case.

πŸ“ƒ Motivating Example

Playground link

export const SOME_CONST = {
  valuenum1: 1,
  valuenum2: 2,
  valuenum3: 3,
}

export type SomeConstType = typeof SOME_CONST

export type Keys = keyof SomeConstType
//          ^? type Keys = "valuenum1" | "valuenum2" | "valuenum3" | "valuenum4" | "valuenum5" | "valuenum6" - ☹️ 

// --------

export type SomeConstType2 = {
  valuenum1: number,
  valuenum2: number,
  valuenum3: number,
}

export type Keys2 = keyof SomeConstType2
//          ^? type Keys2 = keyof SomeConstType2 - πŸ˜„ 

Ref: https://github.com/TypeStrong/typedoc/issues/1867

RyanCavanaugh commented 2 years ago

Is this in reference to declaration emit, quick info (aka tooltips), or something else?

Gerrit0 commented 2 years ago

This is related to quick infos - declaration emit shows what I'd expect, preserving the keyof for both types.

DanKaplanSES commented 1 year ago

Personally, I'd prefer to see // ^? type Keys = "valuenum1" | "valuenum2" | "valuenum3" | "valuenum4" | "valuenum5" | "valuenum6" most of the time. typeof X in quick infos slows/distracts my ability to wrap my head around the types I'm investigating.

Gerrit0 commented 3 months ago

Another example of this where keyof is not necessary:

crossbell.js has a 1400 line as const array which is referenced in some types.

One of those places is in a method parameter

class CharacterContract {
        // ...
    async create(
        {
            owner,
            handle,
            metadataOrUri,
            linkModule,
        }: {
            /** The Ethereum address of the character owner. */
            owner: Address
            /** The handle of the character you want to create. */
            handle: string
            /** The metadata or URI of the character. */
            metadataOrUri: CharacterMetadata | string
            linkModule?: MintOrLinkModuleConfig
        },
        overrides: WriteOverrides<Entry, 'createCharacter'> = {},
    ): Promise<Result<bigint, true>> {

When calling this function, TypeScript does not preserve the Entry alias, which results in a hover which can't really be reasoned about:

image