Skip to content
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

Type-level let expressions for defining complex types #54665

Closed
5 tasks done
ethanresnick opened this issue Jun 16, 2023 · 2 comments
Closed
5 tasks done

Type-level let expressions for defining complex types #54665

ethanresnick opened this issue Jun 16, 2023 · 2 comments

Comments

@ethanresnick
Copy link
Contributor

ethanresnick commented Jun 16, 2023

Suggestion

💻 Use Cases + Motivating Example

Types in TS are getting increasingly complex, as the compiler supports more advanced features and the community gets more comfortable with fancy types. In some cases, though, the defintion for a type can have a lot of duplication in it, of reused type expressions. This can make the type hard to understand, and pulling out the duplicate bits into helper types may add indirection that isn't worth it/also makes things hard to follow. The duplication can also make the type more cumbersome to update/refactor.

Therefore, this feature request would be to have some way to name an intermediate type when defining a type, analogous to let Binding [, Binding ...] in Expression-style expressions in Haskell, Lisp, etc.

My proposed syntax is with type Type [, Type ...] return Type, but that can be bikeshed as needed.

Here's an example, using a type I was writing today. The details don't matter, except that it's large and full of duplication:

Syntax today

type BulkWriteType<TableNames extends BulkWriteTable> = {
  [Table in TableNames]: Simplify<
    {
      [Field in Exclude<
        keyof BulkWriteTableTypes[Table] & string,
        NullableKeysOf<BulkWriteTableTypes[Table]>
      > as Lowercase<Field>]:
          BulkWriteTableTypes[Table][Field] extends ColumnType<any, infer InsertType, any>
            ? InsertType
            : BulkWriteTableTypes[Table][Field];
    } & {
      [Field in NullableKeysOf<BulkWriteTableTypes[Table]> &
        string as Lowercase<Field>]?: 
          BulkWriteTableTypes[Table][Field] extends ColumnType<any, infer InsertType, any>
            ? InsertType
            : BulkWriteTableTypes[Table][Field];
    }
  >;
}[TableNames];

Proposed syntax

type UnsafeBulkWriteType<TableNames extends BulkWriteTable> = {
  [Table in TableNames]: 
    with type 
      FieldNames = keyof BulkWriteTableTypes[Table] & string,
      NullableFieldNames = NullableKeysOf<BulkWriteTableTypes[Table]> & string,
      NonNullableFieldNames = Exclude<FieldNames, NullableFieldNames>,
      FieldInsertType<T> = T extends ColumnType<any, infer InsertType, any> ? InsertType : T
    return {
      [Field in NonNullableFieldNames as Lowercase<Field>]:
        FieldInsertType<BulkWriteTableTypes[Table][Field]>
    } & { 
      [Field in NullableFieldNames as Lowercase<Field>]?: 
        FieldInsertType<BulkWriteTableTypes[Table][Field]> 
    };
}[TableNames];

As you can see, the new syntax not only makes the type much shorter, it also makes it much easier to read and understand.

🔍 Search Terms

temporary type alias let expression types

✅ Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.
@ethanresnick ethanresnick changed the title Haskell-style let expression for defining complex types Type-level let expressions for defining complex types Jun 16, 2023
@jcalz
Copy link
Contributor

jcalz commented Jun 16, 2023

#23188, #41470

@ethanresnick
Copy link
Contributor Author

Ok, I’m gonna close this in favor of the two issues you linked above (as well as #42388).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants