GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Intrinsic string types by ahejlsberg · Pull Request #40580 · microsoft/TypeScript

Conversation

@ahejlsberg

@ahejlsberg Anders Hejlsberg (ahejlsberg) commented Sep 16, 2020 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

This PR introduces four new intrinsic string mapping types in lib.es5.d.ts:

type Uppercase<S extends string> = intrinsic;
type Lowercase<S extends string> = intrinsic;
type Capitalize<S extends string> = intrinsic;
type Uncapitalize<S extends string> = intrinsic;

The new intrinsic keyword is used to indicate that the type alias references a compiler provided implementation. It is an error to specify intrinsic anywhere but immediately following the = separator in a type alias declaration for a type named Uppercase, Lowercase, Capitalize or Uncapitalize taking a single type parameter (but, of course, it is possible that additional intrinsic implementations will be provided in the future). There is generally no reason to ever use intrinsic in user code.

The intrinsic string types behave just like ordinary generic types and are similar to distributive conditional types in that they distribute over union types. Some examples:

type T10 = Uppercase<'hello'>;  // "HELLO"
type T11 = Lowercase<'HELLO'>;  // "hello"
type T12 = Capitalize<'hello'>;  // "Hello"
type T13 = Uncapitalize<'Hello'>;  // "hello"

type T20 = Uppercase<'foo' | 'bar'>;  // "FOO" | "BAR"
type T21 = Lowercase<'FOO' | 'BAR'>;  // "foo" | "bar"
type T22 = Capitalize<'foo' | 'bar'>;  // "Foo" | "Bar"
type T23 = Uncapitalize<'Foo' | 'Bar'>;  // "foo" | "bar"

type T30<S extends string> = Uppercase<`aB${S}`>;
type T31 = T30<'xYz'>;  // "ABXYZ"
type T32<S extends string> = Lowercase<`aB${S}`>;
type T33 = T32<'xYz'>;  // "abxyz"
type T34 = `${Uppercase<'abc'>}${Lowercase<'XYZ'>}`;  // "ABCxyz"

type T40 = Uppercase<string>;  // string
type T41 = Uppercase<any>;  // any
type T42 = Uppercase<never>;  // never
type T43 = Uppercase<42>;  // Error, type 'number' does not satisfy the constraint 'string'

Note that the Capitalize<S> and Uncapitalize<S> intrinsic types could fairly easily be implemented in pure TypeScript using conditional types and template literal type inference, but it isn't practical to do so at the moment because we use ESLint which hasn't yet been updated to support template literal types (though we expect that to happen soon).

The intrinsic string types replace the uppercase, lowercase, capitalize, and uncapitalize modifiers in template literal types (based on feedback in #40336). This PR removes those modifiers.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

meritozh, alexrock, ethanresnick, kotarella1110, robbiespeed, treybrisbane, groege, interphx, g-plane, Jack-Works, and 41 more reacted with thumbs up emoji demensky, hinell, and shilangyu reacted with thumbs down emoji ExE-Boss, luketanner, and timargra reacted with hooray emoji worstpractice, treybrisbane, groege, lffg, VitorLuizC, lizraeli, ExE-Boss, fvilante, svieira, luketanner, and 4 more reacted with heart emoji ExE-Boss, Xunnamius, and vnylbscr reacted with rocket emoji uhyo, lffg, VitorLuizC, SlurpTheo, hinell, and winexy reacted with eyes emoji
All reactions
  • 👍 51 reactions
  • 👎 3 reactions
  • 🎉 3 reactions
  • ❤️ 14 reactions
  • 🚀 3 reactions
  • 👀 6 reactions
Anders Hejlsberg (ahejlsberg) added 11 commits September 14, 2020 14:53
@ahejlsberg
Introduce Uppercase<T> and Lowercase<T> intrinsic types
6b25c17
@ahejlsberg
Accept new API baselines
59a9049
@ahejlsberg
Add Uppercase/Lowercase/Capitalize/Uncapitalize to lib.d.ts
93bf6c2
@ahejlsberg
Update fourslash
e239db5
@ahejlsberg
Add an 'intrinsic' keyword
68717bc
@ahejlsberg
Update template literal type tests
d6108ac
@ahejlsberg
Accept new API baselines
7167b25
@ahejlsberg
Minor fixes
5230d84
@ahejlsberg
Switch Capitalize<T> and Uncapitalize<T> to intrinsic types
aa17b5e
@ahejlsberg
Add tests
b55d3f3
@ahejlsberg
Accept new baselines
b9c395d
@typescript-bot TypeScript Bot (typescript-bot) added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Sep 16, 2020
@ahejlsberg
Merge branch 'master' into intrinsicStringTypes
6ef76a0
# Conflicts:
#	src/compiler/diagnosticMessages.json
@ahejlsberg
Anders Hejlsberg (ahejlsberg) requested review from Daniel Rosenwasser (DanielRosenwasser) and Ryan Cavanaugh (RyanCavanaugh) September 16, 2020 01:48
@ahejlsberg
Accept new baselines
de5e589
@ethanresnick

Ethan Resnick (ethanresnick) commented Sep 16, 2020

Copy link
Copy Markdown
Contributor

Total bikeshedding and possibly a bad idea, but... would it make any sense to re-use declare, as in:

declare type Capitalize<T extends string>;

I don't know declare's current semantics well enough to say whether that's a natural extension, or whether it's a confusing jumble that would overload declare with too many similar, but not quite close enough, meanings. Or maybe this already means something else. But just a thought.

treykasada, treybrisbane, yaroslavvoloshchuk-codaio, felixfbecker, and KisaragiEffective reacted with thumbs up emoji
All reactions
  • 👍 5 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Anders Hejlsberg (ahejlsberg) added 3 commits September 16, 2020 07:36
@ahejlsberg
Remove template literal type casing modifiers
a5b3c61
@ahejlsberg
Update tests
a405006
@ahejlsberg
Accept new baselines
b46b3ec
@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Sep 16, 2020

Copy link
Copy Markdown
Member Author

Would it make any sense to re-use declare...

That's certainly looks reasonable, but unlike the current intrinsic solution, it would require a tool chain update because our grammar currently always requires an = and a type in a type alias declaration. For example, we'd need ESLint to be updated.

ethanresnick reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

DanielRosenwasser
Daniel Rosenwasser (DanielRosenwasser) requested changes Sep 16, 2020
View reviewed changes

@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) left a comment •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we get some tests for intrinsic being used outside of the immediate type alias context?

let a1: intrinsic;
let a2: { intrinsic: intrinsic };
let intrinsic: intrinsic.intrinsic;
type Foo = (intrinsic);
type Foo<intrinsic> = intrinsic;
type Foo<T extends intrinsic> = T;
type Foo<intrinsic extends intrinsic> = intrinsic;
type Bar<intrinsic extends intrinsic> = (intrinsic);

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@weswigham

Wesley Wigham (weswigham) commented Sep 16, 2020

Copy link
Copy Markdown
Member
type intrinsic = string;
let a1: intrinsic = "ok";

should probably also be checked to still work, right?

ethanresnick reacted with thumbs up emoji treykasada and Manu1400 reacted with eyes emoji
All reactions
  • 👍 1 reaction
  • 👀 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@rbuckton

Ron Buckton (rbuckton) commented Sep 16, 2020

Copy link
Copy Markdown
Contributor

As with the declare suggestion, I would still like to express my preference for extern type Uppercase<T extends string>; it still would cause issues with tooling, but we could look into what's necessary to put together and use a "private" build of typescript-eslint to handle that.

treykasada, cpplearner, treybrisbane, ExE-Boss, chocolateboy, and crazytoucan reacted with thumbs up emoji
All reactions
  • 👍 6 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@DanielRosenwasser

Daniel Rosenwasser (DanielRosenwasser) commented Sep 16, 2020 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Tooling catchup concerns aren't reasonable blockers in my opinion. We could get the self-hosting blockers out of the way ahead of time if we really needed, and that's the right workflow and right way to work with the community.

ExE-Boss and felixfbecker reacted with thumbs up emoji
All reactions
  • 👍 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@rbuckton

Ron Buckton (rbuckton) commented Sep 16, 2020

Copy link
Copy Markdown
Contributor

The only "tooling catchup concern" I think we're discussing is regarding tooling that out team depends on to build the product. Adding new syntax often requires a full release cycle lag before we can actually use that syntax in our declaration files, given that we use eslint and typescript-eslint to lint those files.

When we added compound assignment operators, we couldn't use them in our own codebase until typescript-eslint/typescript-eslint#2253 was merged, and that required us to use a nightly build until the next official release went out. At the very least, making PRs against typescript-eslint to unblock ourselves still benefits the community as it is one fewer tool in the toolchain that users will need to wait for before they can upgrade to a newer version of TypeScript.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@rbuckton

Ron Buckton (rbuckton) commented Sep 16, 2020

Copy link
Copy Markdown
Contributor

The typescript-eslint concern is also the reason why we are looking for a way to externalize the type for Capitalize, as implementing Capitalize in terms of Uppercase using template literal types also breaks typescript-eslint due to the newer syntax.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@treybrisbane

Trey Brisbane (treybrisbane) commented Sep 17, 2020

Copy link
Copy Markdown

I really like this approach. Much more than the previous "magic modifiers"! 🙂

Having a relatively general solution for special-casing complex typing operations without the need for new syntax, that also gives you a free migration path if/when the language becomes expressive enough to implement them outside the compiler, seems like a win for everybody.

Jack-Works, ExE-Boss, fvilante, jirutka, mpawelski, rhermes62, and LorenzHenk reacted with thumbs up emoji
All reactions
  • 👍 7 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@treybrisbane Trey Brisbane (treybrisbane) mentioned this pull request Sep 17, 2020
Allow pattern literal types like http://${string} to exist and be reasoned about #40598
Merged
@ahejlsberg
Add more tests
d5980d1
Anders Hejlsberg (ahejlsberg) added 2 commits September 20, 2020 14:32
@ahejlsberg
Merge branch 'master' into intrinsicStringTypes
db2740e
@ahejlsberg
Update tests
4031f47
Jack-Works
Jack Works (Jack-Works) reviewed Sep 21, 2020
View reviewed changes
Comment thread src/compiler/checker.ts
Uncapitalize
}

const intrinsicTypeKinds: ReadonlyESMap<string, IntrinsicTypeKind> = new Map(getEntries({

@Jack-Works Jack Works (Jack-Works) Sep 21, 2020

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also change ThisType into an intrinsic?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) Sep 21, 2020

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? ThisType is just a marker type that doesn't have any effect on its type argument. No reason to change it.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@Jack-Works Jack Works (Jack-Works) Sep 21, 2020

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought "intrinsic" means "hey I'm compiler magic" and ThisType is a compiler magic too 👀

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

weswigham and HerringtonDarkholme reacted with thumbs up emoji
All reactions
  • 👍 2 reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) Sep 21, 2020 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several "magic" types already that aren't intrinsic. For example Object, Function, Array<T>, Promise<T>, Iterator<T>, and others all receive special treatment in one way or another. The particular role "intrinsic" plays is to indicate that the implementation of the type is provided by the compiler. In the case of ThisType, there really is nothing interesting about the implementation because ThisType<T> is the same as T. All we do with ThisType is to activate certain behaviors when a reference to it occurs in a contextual type, but otherwise there's nothing special about it.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Jack-Works, ExE-Boss, fvilante, ludamad, tobiasletcher-ap, Gerrit0, anuraghazra, and newraina reacted with thumbs up emoji
All reactions
  • 👍 8 reactions
RyanCavanaugh
Ryan Cavanaugh (RyanCavanaugh) approved these changes Sep 21, 2020
View reviewed changes
@ahejlsberg
Anders Hejlsberg (ahejlsberg) merged commit fbce4f6 into master Sep 21, 2020
@ahejlsberg
Anders Hejlsberg (ahejlsberg) deleted the intrinsicStringTypes branch September 21, 2020 17:09
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Sep 21, 2020
Template literal types and mapped type 'as' clauses #40336
Merged
@Jack-Works Jack Works (Jack-Works) mentioned this pull request Sep 22, 2020
Throw types #40468
Closed
@trusktr

Joe Pea (trusktr) commented Sep 22, 2020 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Yeah, it would be great to also have DashCase and CamelCase and similar!

In the DOM libs it is common to map from JS camelCase properties to DOM dash-case attributes (f.e. el.fooBar = 123 and <el foo-bar="123">), or CapitalizedClass names to dash-case custom element names (f.e. class FooBar extends HTMLElement and <foo-bar>).

ExE-Boss, IllusionMH, Jack-Works, KyleDavidE, blfunex, limulus, jogibear9988, Raiondesu, AudunWA, TwoToneBytes, and 2 more reacted with thumbs up emoji blfunex and aaroncowie reacted with eyes emoji
All reactions
  • 👍 12 reactions
  • 👀 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mmmveggies mmmveggies mentioned this pull request Oct 2, 2020
DashCase and CamelCase intrinsic string types, or similar #40710
Open
5 tasks done
@JLHwung Huáng Jùnliàng (JLHwung) mentioned this pull request Oct 2, 2020
Support TypeScript mapped type 'as' clauses babel/babel#12129
Merged
@mmmveggies mmmveggies mentioned this pull request Oct 2, 2020
Consider custom vs standard library only for intrinsic type functions #40915
Closed
@btoo btoo mentioned this pull request Oct 6, 2020
Suggestion: Range as Number type #15480
Closed
@nicolo-ribaudo Nicolò Ribaudo (nicolo-ribaudo) mentioned this pull request Oct 6, 2020
[ts] Add support for the "intrinsic" keyword babel/babel#12147
Merged
This was referenced Oct 9, 2020
Math with Number Literal Type #26382
Open
Utility type to reference JSDoc of another type #41023
Open
@tjjfvi T6 (tjjfvi) mentioned this pull request Oct 22, 2020
Feature Request: Documented Utility Type #41165
Open
5 tasks done
@Eyas Eyas (Eyas) mentioned this pull request Oct 25, 2020
Type errors against Google's recommended Schema JSON-LD's google/schema-dts#130
Closed
@g-plane Pig Fang (g-plane) mentioned this pull request Oct 29, 2020
[typescript] 4.1 intrinsic keyword swc-project/swc#1193
Merged
@voxpelli Pelle Wessman (voxpelli) mentioned this pull request Oct 29, 2020
Augment flag keys in ".flags" type definition sindresorhus/meow#155
Closed
@joepvl

Joep van Liempd (joepvl) commented Nov 18, 2020

Copy link
Copy Markdown

FWIW, if you want to play around with these types in https://www.typescriptlang.org/play @ v4.1.0-beta, you can emulate them using the following:

type Uncapitalize<S extends string> = `${uncapitalize S}`;
type Capitalize<S extends string> = `${capitalize S}`;
type Lowercase<S extends string> = `${lowercase S}`;
type Uppercase<S extends string> = `${uppercase S}`;
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@radiosilence

james cleveland (radiosilence) commented Nov 20, 2020 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Think we can add something like Camelize or SnakeCase to convert from snake_case to camelCase etc? It would be cool if we could define our own somehow.

Would be really sweet if we could finally type a recursive camelize function.

This does it for a fixed word length:

type B = 'hi_mate';
type Camelize<S extends string> = S extends `${infer A}_${infer B}`
  ? `${A}${Capitalize<B>}`
  : unknown;

type C = Camelize<B>;

Is there a way to use spread to do it for many?

Have managed to do it like this but it's hideous:

type Z = 'hi_mate_what_up_good_morning_today';

type Camelize<
  S
> = S extends `${infer A}_${infer B}_${infer C}_${infer D}_${infer E}_${infer F}_${infer G}_${infer H}`
  ? `${A}${Capitalize<B>}${Capitalize<C>}${Capitalize<D>}${Capitalize<E>}${Capitalize<F>}${Capitalize<G>}${Capitalize<H>}`
  : S extends `${infer A}_${infer B}_${infer C}_${infer D}_${infer E}_${infer F}_${infer G}`
  ? `${A}${Capitalize<B>}${Capitalize<C>}${Capitalize<D>}${Capitalize<E>}${Capitalize<F>}${Capitalize<G>}`
  : S extends `${infer A}_${infer B}_${infer C}_${infer D}_${infer E}_${infer F}`
  ? `${A}${Capitalize<B>}${Capitalize<C>}${Capitalize<D>}${Capitalize<E>}${Capitalize<F>}`
  : S extends `${infer A}_${infer B}_${infer C}_${infer D}_${infer E}`
  ? `${A}${Capitalize<B>}${Capitalize<C>}${Capitalize<D>}${Capitalize<E>}`
  : S extends `${infer A}_${infer B}_${infer C}_${infer D}`
  ? `${A}${Capitalize<B>}${Capitalize<C>}${Capitalize<D>}`
  : S extends `${infer A}_${infer B}_${infer C}`
  ? `${A}${Capitalize<B>}${Capitalize<C>}`
  : S extends `${infer A}_${infer B}`
  ? `${A}${Capitalize<B>}`
  : S;

type C = Camelize<Z>; // hiMateWhatUpGoodMorningToday

function upperFirst<S extends string>(s: S) {
  const [head, ...tail] = s;
  return [head.toUpperCase(), ...tail].join('') as Capitalize<S>;
}

const z = upperFirst('hello');

function camelCase<S extends string>(s: S) {
  const [head, ...tail] = s.split('_');
  return `${head}${tail.map(upperFirst).join('')}` as Camelize<S>;
}

const y = camelCase('hello_mate');

So far so good, now I just have to figure out a way of doing the typing recursively on an object.

Ok, I think I nailed it:

type DeepCamelize<T> = {
  [K in keyof T as Camelize<K>]: DeepCamelize<T[K]>;
};

Would still be nice to get: Same in reverse, and maybe an implementation that uses spread.

Ok, improved version usingJoin and a modified splitter:

type CapitalizeIf<
  Condition extends boolean,
  T extends string
> = Condition extends true ? Capitalize<T> : T;

type SplitCamel<
  S extends string,
  D extends string,
  IsTail extends boolean = false
> = string extends S
  ? string[]
  : S extends ''
  ? []
  : S extends `${infer T}${D}${infer U}`
  ? [CapitalizeIf<IsTail, T>, ...SplitCamel<U, D, true>]
  : [CapitalizeIf<IsTail, S>];

type Camelize<S> = S extends string ? Join<SplitCamel<S, '_'>, ''> : S;


type DeepCamelize<T> = {
  [K in keyof T as Camelize<K>]: DeepCamelize<T[K]>;
};
anuraghazra and m3thom reacted with thumbs up emoji
All reactions
  • 👍 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Sheetal Nandi (sheetalkamat) added a commit to microsoft/TypeScript-TmLanguage that referenced this pull request Nov 20, 2020
@sheetalkamat
Support for intrinsic keyword in type alias as part of microsoft/Type…
a755c6b
…Script#40580
@aboyton

Andrew Boyton (aboyton) commented Nov 24, 2020

Copy link
Copy Markdown

For camelCase, this seems to be an example in the PR that added this feature. https://github.com/microsoft/TypeScript/pull/40336/files#diff-4c1d1a787d1d286623e4419c6b614fc45fc6f65d7ff4efde25e5d145f9e0c654R84

type SnakeToCamelCase<S extends string> =
    S extends `${infer T}_${infer U}` ? `${lowercase T}${SnakeToPascalCase<U>}` :
    S extends `${infer T}` ? `${lowercase T}` :
    SnakeToPascalCase<S>;

type SnakeToPascalCase<S extends string> =
    string extends S ? string :
    S extends `${infer T}_${infer U}` ? `${capitalize `${lowercase T}`}${SnakeToPascalCase<U>}` :
    S extends `${infer T}` ? `${capitalize `${lowercase T}`}` :
    never;

type RR0 = SnakeToPascalCase<'hello_world_foo'>;  // 'HelloWorldFoo'
type RR1 = SnakeToPascalCase<'FOO_BAR_BAZ'>;  // 'FooBarBaz'
type RR2 = SnakeToCamelCase<'hello_world_foo'>;  // 'helloWorldFoo'
type RR3 = SnakeToCamelCase<'FOO_BAR_BAZ'>;  // 'fooBarBaz'

This could be useful to be shared inside TypeScript itself.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@softoika softoika mentioned this pull request Nov 24, 2020
110 - Capitalize<T> type-challenges/type-challenges#374
Open
@andrewthauer Andrew Thauer (andrewthauer) mentioned this pull request Nov 25, 2020
Pagerduty plugin backstage/backstage#3317
Merged
4 tasks done
@mikaelwaltersson

Mikael Waltersson (mikaelwaltersson) commented Feb 16, 2021 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

This should work for strings, objects and arrays:

type ToCamelCase<T> =
    T extends `${infer A}_${infer B}`
        ? `${Uncapitalize<A>}${Capitalize<ToCamelCase<B>>}` :
    T extends string
        ? Uncapitalize<T> :
    T extends (infer A)[]
        ? ToCamelCase<A>[] :
    T extends {}
        ?  { [K in keyof T as ToCamelCase<K>]: ToCamelCase<T[K]>; } :
    T;
type ToSnakeCase<T> =
    T extends `${infer A}${infer B}${infer C}`
        ? (
           [A, B, C] extends [Lowercase<A>, Exclude<Uppercase<B>, '_'>, C]
                ? `${A}_${Lowercase<B>}${ToSnakeCase<C>}`
                : `${Lowercase<A>}${ToSnakeCase<`${B}${C}`>}`
        ) :
    T extends string
        ? Lowercase<T> :
    T extends (infer A)[]
        ? ToSnakeCase<A>[] :
    T extends {}
        ?  { [K in keyof T as ToSnakeCase<K>]: ToSnakeCase<T[K]>; } :
    T;
ycjcl868 and hudson-newey reacted with thumbs up emoji
All reactions
  • 👍 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@github-actions github-actions Bot mentioned this pull request Jul 14, 2021
Translate 1 file to ko - TypeScript 4.1.md microsoft/TypeScript-Website-Localizations#94
Closed
@jcalz Joe Calzaretta (jcalz) mentioned this pull request Jul 17, 2023
A Type Error occurs with using a generic that extends a branded intrinsic type #55040
Closed
@microsoft Microsoft (microsoft) locked as resolved and limited conversation to collaborators Oct 21, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) DanielRosenwasser requested changes
@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) RyanCavanaugh approved these changes
+1 more reviewer
@Jack-Works Jack Works (Jack-Works) Jack-Works left review comments
Reviewers whose approvals may not affect merge requirements

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

15 participants

@ahejlsberg @ethanresnick @weswigham @rbuckton @DanielRosenwasser @treybrisbane @Jack-Works @KSXGitHub @trusktr @joepvl @radiosilence @aboyton @mikaelwaltersson @RyanCavanaugh @typescript-bot
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.