GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||
GeezerVison is showing github.com — Intrinsic string types by ahejlsberg · Pull Request #40580 · microsoft/TypeScript Conversation
|
|
Total bikeshedding and possibly a bad idea, but... would it make any sense to re-use 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. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
That's certainly looks reasonable, but unlike the current |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem 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.
There was an error while loading. Please reload this page.
type intrinsic = string; let a1: intrinsic = "ok"; should probably also be checked to still work, right? |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
As with the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
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. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
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 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 |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
The |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
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. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| Uncapitalize | ||
| } | ||
|
|
||
| const intrinsicTypeKinds: ReadonlyESMap<string, IntrinsicTypeKind> = new Map(getEntries({ |
There was a problem 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.
There was an error while loading. Please reload this page.
There was a problem 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.
There was an error while loading. Please reload this page.
There was a problem 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.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem 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.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Yeah, it would be great to also have In the DOM libs it is common to map from JS camelCase properties to DOM dash-case attributes (f.e. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
FWIW, if you want to play around with these types in https://www.typescriptlang.org/play @ 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}`; |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Think we can add something like 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 using 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]>; }; |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
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. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
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; |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.