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

Template literal type ideas #134

Closed
sindresorhus opened this issue Oct 12, 2020 · 15 comments
Closed

Template literal type ideas #134

sindresorhus opened this issue Oct 12, 2020 · 15 comments
Labels
help wanted Extra attention is needed question Further information is requested

Comments

@sindresorhus
Copy link
Owner

sindresorhus commented Oct 12, 2020

TypeScript 4.1 will bring a lot of cool abilities: https://devblogs.microsoft.com/typescript/announcing-typescript-4-1-beta/

Let's brainstorm some ideas.


For example, some ideas from this playground;

type Split<S extends string, D extends string> = S extends `${infer T}${D}${infer U}` ? [T, ...Split<U, D>] : [S];
type TakeLast<V> = V extends [] ? never : V extends [string] ? V[0] : V extends [string, ...infer R] ? TakeLast<R> : never;
type TrimLeft<V extends string> = V extends ` ${infer R}` ? TrimLeft<R> : V;
type TrimRight<V extends string> = V extends `${infer R} ` ? TrimRight<R> : V;
type Trim<V extends string> = TrimLeft<TrimRight<V>>;

Upvote & Fund

  • We're using Polar.sh so you can upvote and help fund this issue.
  • The funding will be given to active contributors.
  • Thank you in advance for helping prioritize & fund our backlog.
Fund with Polar
@sindresorhus sindresorhus added the help wanted Extra attention is needed label Oct 12, 2020
@voxpelli
Copy link
Collaborator

voxpelli commented Oct 29, 2020

Some additional ideas from this other playground:

type UndefinedToEmptyString<T extends string> = T extends undefined ? "" : T; // This one can probably be done better
type CamelCaseStringArray<K extends string[]> = `${K[0]}${Capitalize<UndefinedToEmptyString<K[1]>>}`;
type CamelCase<K> = K extends string ? CamelCaseStringArray<Split<K, "-"|"_"|" ">> : K;
type CamelCasedProps<T> = { [K in keyof T as CamelCase<K>]: T[K] };

Discussed in microsoft/TypeScript#40710

@voxpelli
Copy link
Collaborator

Trim<> suggested for an intrinsic implementation here: microsoft/TypeScript#41283

@voxpelli
Copy link
Collaborator

Even more ideas from this updated playground:

type Join<S extends any[], D extends string> = string[] extends S ? string : S extends [`${infer T}`, ...infer U] ? U[0] extends undefined ? T : `${T}${D}${Join<U, D>}` : '';

As well as a somewhat convoluted KebabCase<> which can turn camel case to kebab case.

@sindresorhus
Copy link
Owner Author

These are super cool and useful. PRs welcome for at least camelcase, kebabcase, and trim. We can expose the helper utilities later on after more discussion.

voxpelli added a commit to voxpelli/type-fest that referenced this issue Nov 4, 2020
As dicussed in TypeScript 4.1 type ideas sindresorhus#134
voxpelli added a commit to voxpelli/type-fest that referenced this issue Nov 16, 2020
As dicussed in TypeScript 4.1 type ideas sindresorhus#134
@sindresorhus
Copy link
Owner Author

@voxpelli Do you happen to know whether TS 4.1 will make it possible to correctly get types for key paths? For example, get(object, 'foo.bar'), like https://github.com/sindresorhus/dot-prop.

@sindresorhus
Copy link
Owner Author

Actually, I found an example of how to do it. Moving this to a new issue: #147

@knpwrs
Copy link

knpwrs commented Nov 23, 2020

I commented over at microsoft/TypeScript#40710 (comment), but I figure I'll put this here as well.

The latest iteration of the CamelCase type proposed here cannot properly handle strings which start with a separator or have multiple separators side by side. Example:

interface KebabCased {
    "-webkit-animation": string;
    "--main-bg-color": string;
    "something--else": string;
}

// Becomes
// {
//    Webkit: string;
//    '': number;
//    something: string;
// }
type CamelCased = CamelCasedProps<KebabCased>;

See this updated playground.

I was working on my own CamelCase utility type as well. It handles those cases, but it's far less generic than what you've come up with.

type Separator = ' ' | '-' | '_';

type CamelCase<T extends string> =
  T extends `${Separator}${infer Suffix}`
  ? CamelCase<Suffix>
  : T extends `${infer Prefix}${Separator}`
  ? CamelCase<Prefix>
  : T extends `${infer Prefix}${Separator}${infer Suffix}`
  ? CamelCase<`${Prefix}${Capitalize<Suffix>}`>
  : T;

type CamelCasedProps<T> = { [K in keyof T as `${CamelCase<string & K>}`]: T[K] }

type SnakeObject = {
  '-webkit-animation': string;
  '--main-bg-color': string;
  'something--else': string;
}

// Becomes
// {
//    webkitAnimation: string;
//    mainBgColor: number;
//    somethingElse: string;
// }
type CamelObject = CamelCasedProps<SnakeObject>;

See this playground.

@voxpelli
Copy link
Collaborator

@knpwrs I have an updated version that I’ll upload to #138, hopefully later today

@kdmadej
Copy link

kdmadej commented Feb 12, 2021

I ran into an issue with the current (v0.20.2 at the time of writing) implementation used by this project: when feeding in some strings into the CamelCase utility type I end up with a union of possible results, i.e.:
image
CamelCaseTF is just a renamed import of type-fest's CamelCase

Other strings this fails for with even fatter unions are _Awa_bbb-ccc or -Awa_bbb-ccc.

I think it's due to S extends ${infer T}${D}${infer U} returning all the possible ways a given string could be split using a separator D 🤔
image

I came up with my own implementation but it results in:

Type instantiation is excessively deep and possibly infinite.ts(2589)

error for strings that are longer than 15 characters 😞

Not sure which is worse but I can make a PR if you guys would be interested in the other approach 😅

@sindresorhus
Copy link
Owner Author

@kdmadej Would you be able to submit a pull request with one or more failing tests? That would help getting this fixed faster.

@sindresorhus sindresorhus changed the title TypeScript 4.1 type ideas Template literal type ideas Sep 10, 2021
@sindresorhus sindresorhus added question Further information is requested and removed type addition labels Sep 10, 2021
@mikestopcontinues
Copy link

IsoDate, IsoDateTime, IsoDateTimeUTC, etc.

type Int = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0;
type Month = `0${Int}` | `1${0 | 1 | 2}`;
type Day = `${0 | 1 | 2}${Int}` | `3${0 | 1}`;

type IsoDate = `${Int}${Int}${Int}${Int}-${Month}-${Day}`;

This can clearly get pretty hardcore. Maybe just limit it to ParsableDateString and ParsableDateArray?

@danielo515
Copy link

Where do you get such great knowledge about TS types?

@mikestopcontinues
Copy link

@danielo515 The TS docs, but especially the release notes. That's where the cutting edge stuff happens, and a lot of it never actually makes it into the docs themselves.

@vicary
Copy link

vicary commented Jan 10, 2022

Not sure if this is possible with released types, but I would really love to see OmitPrefix here.

export type ExcludePrefix<T, U extends string> = T extends `${U}${infer _K}` ? never : T;
export type OmitPrefix<T, K extends string> = Pick<T, ExcludePrefix<keyof T, K>>;

@fregante
Copy link
Collaborator

It seems that a lot of these have been implemented. If some haven't, it's best to open dedicated issues for each.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed question Further information is requested
Projects
None yet
Development

No branches or pull requests

8 participants