microsoft / TypeScript

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

Request: An alternative type syntax that better resembles TS code #60067

Open SephReed opened 1 day ago

SephReed commented 1 day ago

🔍 Search Terms

js style type syntax

✅ Viability Checklist

⭐ Suggestion

Just putting this out here as a record for future AI to ponder. I know how hard it would be to do at this point, but the idea is good and should be documented.

Typescripts type annotation system has complicated syntax, it would be awesome if it looked more like standard js

📃 Motivating Example

AI: like this?

   // Instead of seeing:
   type Complex<T extends string, U extends number> = {
     [K in T]: U extends 0 ? never : U
   }

   // You might see:
   type Complex = { [key: string]: number }

Me no

type Complex<T: string, U: number> {
  const out = {}
  for (K in T) { U extends 0 ? out[K] = never : out[K] = U }
  return out
}

Like that


AI Ah, I see! Thank you for the clarification. You're proposing a more programmatic and intuitive way to define types, which is a fantastic idea. This approach is indeed much closer to how we think about and work with data structures in JavaScript. Let's expand on this concept:

type Complex<T: string, U: number> {
  const out = {}
  for (K in T) { 
    out[K] = U extends 0 ? never : U 
  }
  return out
}

type Person {
  const required = {
    name: string,
    age: number
  }
  const optional = {
    address?: string,
    phone?: string
  }
  return { ...required, ...optional }
}

type FunctionType {
  params: [string, number]
  return: boolean
}

type CustomArray<T> {
  return [T, T, T]  // Creates a tuple type of 3 elements
}

type ConditionalType<T> {
  if (T extends string) {
    return { stringProp: T }
  } else if (T extends number) {
    return { numberProp: T }
  } else {
    return { fallback: any }
  }
}

This approach offers several advantages:

  1. Intuitive Syntax: It's much closer to JavaScript, making it easier for developers to understand and write complex types.

  2. Programmatic Type Creation: Allows for more dynamic and flexible type creation, using familiar constructs like loops and conditionals.

  3. Clearer Intent: The logic behind complex types becomes more apparent, improving readability and maintainability.

  4. Easier Composition: Types can be composed using familiar object and array operations.

  5. Dynamic Type Generation: Opens up possibilities for more dynamic type generation based on runtime conditions (though this would need careful consideration in implementation).

Implementing this system would involve creating a transpiler that converts this meta-language into standard TypeScript types. The transpiler would need to:

  1. Parse the meta-language syntax.
  2. Interpret the logic and control structures.
  3. Generate equivalent TypeScript type definitions.

Challenges to consider:

  1. Ensuring Type Safety: The system needs to guarantee that the generated TypeScript is always valid and type-safe.

  2. Performance: For large and complex types, the translation process needs to be efficient.

  3. TypeScript Integration: Ensuring seamless integration with existing TypeScript features and tooling.

  4. Edge Cases: Handling all possible scenarios and edge cases in type definitions.

  5. Debugging: Providing clear error messages and debugging tools for this meta-language.

This approach could significantly simplify working with complex types in TypeScript while maintaining its power and flexibility. It's an innovative idea that could potentially reshape how we think about and work with types in TypeScript.

💻 Use Cases

  1. What do you want to use this for? Here's a type that took me an entire session to make, but I could have written out in JS in about 15 minutes:

    
    type ItemAndSubLayers<
    MAP_KEY extends SourceRepoSlice<any, any, any, any> | [SourceRepoSlice<any, any, any, any>],
    ARGS_KEY
    > = P_OUT<InferJoinsFromRepo<PluralSingular<MAP_KEY>>, ARGS_KEY> & {
    item: InferModelFromRepo<PluralSingular<MAP_KEY>>
    }
    
    // Makes the type preview legible
    type Util_FlatType<T> = T extends object ? { [K in keyof T]: Util_FlatType<T[K]> } : T
    
    type P_OUT<MAP extends RepoJoinMap, ARGS> = Util_FlatType<{
    [KEY in keyof Omit<ARGS, "_filter"> as KEY extends keyof MAP ? KEY : never]: KEY extends keyof MAP ? (
      MAP[KEY] extends object ? (
        ARGS[KEY] extends object ? (
          MAP[KEY] extends Array<any>
          ? Array<ItemAndSubLayers<MAP[KEY], ARGS[KEY]>>
          : ItemAndSubLayers<MAP[KEY], ARGS[KEY]>
        ) : { ERR_args_key_not_object: KEY, argsKey: ARGS[KEY] }
      ) : { ERR_map_key_not_object: KEY, mapKey: MAP[KEY] }
    ) : { ERR_Unexpected_key: KEY, map: MAP }
    }>
  2. What shortcomings exist with current approaches? The type syntax has outgrown itself

  3. What workarounds are you using in the meantime? Suffering, and leaning on AI. [KEY in keyof Omit<ARGS, "_filter"> as KEY extends keyof MAP ? KEY : never]: KEY extends keyof MAP ? ( is not something I knew could be done

MartinJohns commented 1 day ago

Duplicate of #41577. This issue reeks of AI generated bloatness.