1
0
mirror of https://github.com/robonen/tools.git synced 2026-03-20 10:54:44 +00:00

refactor(packages/stdlib): change getByPath type to string and add comments to template types

This commit is contained in:
2024-05-30 02:51:08 +07:00
parent ba68e293b9
commit d0c74be856
3 changed files with 33 additions and 13 deletions

View File

@@ -44,7 +44,7 @@ type Path<T> = T extends `${infer Key}.${infer Rest}`
// Output: { a: { b: { c: PropertyKey } } } // Output: { a: { b: { c: PropertyKey } } }
export type UnionToIntersection<Union> = ( export type UnionToIntersection<Union> = (
Union extends unknown Union extends unknown
? (distributedUnion: Union) => void ? (distributedUnion: Union) => void
: never : never
) extends ((mergedIntersection: infer Intersection) => void) ) extends ((mergedIntersection: infer Intersection) => void)
@@ -62,7 +62,7 @@ type PathToType<T extends string[]> = T extends [infer Head, ...infer Rest]
? { [K in Head & string]: PathToType<Rest> } ? { [K in Head & string]: PathToType<Rest> }
: never : never
: never : never
: unknown; : string;
export type Generate<T extends string> = UnionToIntersection<PathToType<Path<T>>>; export type Generate<T extends string> = UnionToIntersection<PathToType<Path<T>>>;
type Get<O, K> = GetWithArray<O, Path<K>>; type Get<O, K> = GetWithArray<O, Path<K>>;

View File

@@ -33,10 +33,16 @@ describe('templateObject', () => {
// }); // });
it('replace template placeholders with nested values from args', () => { it('replace template placeholders with nested values from args', () => {
const template = 'Hello, {user.name}!'; const result = templateObject('Hello {{user.name}, your address {user.addresses.0.street}', {
const args = { user: { name: 'John' } }; user: {
const result = templateObject(template, args); name: 'John Doe',
addresses: [
{ street: '123 Main St', city: 'Springfield'},
{ street: '456 Elm St', city: 'Shelbyville'}
]
}
});
expect(result).toBe('Hello, John!'); expect(result).toBe('Hello {John Doe, your address 123 Main St');
}); });
}); });

View File

@@ -1,4 +1,5 @@
import { getByPath, type Generate } from '../../arrays'; import { getByPath, type Generate } from '../../arrays';
import { isFunction } from '../../types';
/** /**
* Type of a value that will be used to replace a placeholder in a template. * Type of a value that will be used to replace a placeholder in a template.
@@ -20,6 +21,13 @@ type TemplateArgsObject = StringPrimitive[] | { [key: string]: TemplateArgsObjec
*/ */
const TEMPLATE_PLACEHOLDER = /{([^{}]+)}/gm; const TEMPLATE_PLACEHOLDER = /{([^{}]+)}/gm;
/**
* Removes the placeholder syntax from a template string.
*
* @example
* type Base = ClearPlaceholder<'{user.name}'>; // 'user.name'
* type Unbalanced = ClearPlaceholder<'{user.name'>; // 'user.name'
*/
export type ClearPlaceholder<T extends string> = export type ClearPlaceholder<T extends string> =
T extends `${string}{${infer Template}` T extends `${string}{${infer Template}`
? ClearPlaceholder<Template> ? ClearPlaceholder<Template>
@@ -27,6 +35,12 @@ export type ClearPlaceholder<T extends string> =
? ClearPlaceholder<Template> ? ClearPlaceholder<Template>
: T; : T;
/**
* Extracts all placeholders from a template string.
*
* @example
* type Base = ExtractPlaceholders<'Hello {user.name}, {user.addresses.0.street}'>; // 'user.name' | 'user.addresses.0.street'
*/
export type ExtractPlaceholders<T extends string> = export type ExtractPlaceholders<T extends string> =
T extends `${infer Before}}${infer After}` T extends `${infer Before}}${infer After}`
? Before extends `${string}{${infer Placeholder}` ? Before extends `${string}{${infer Placeholder}`
@@ -35,18 +49,18 @@ export type ExtractPlaceholders<T extends string> =
: never; : never;
export function templateObject<T extends string, A extends Generate<ExtractPlaceholders<T>>>(template: T, args: A, fallback?: TemplateFallback): string { export function templateObject<T extends string, A extends Generate<ExtractPlaceholders<T>>>(template: T, args: A, fallback?: TemplateFallback): string {
return template.replace(TEMPLATE_PLACEHOLDER, (subs, key) => { return template.replace(TEMPLATE_PLACEHOLDER, (_, key) => {
return getByPath(args, key) as string; const value = getByPath(args, key) as string;
// return value !== undefined ? value : (isFunction(fallback) ? fallback(key) : fallback); return value !== undefined ? value : (isFunction(fallback) ? fallback(key) : '');
}); });
} }
templateObject('Hello {user.name}, your address {user.addresses.0}', { templateObject('Hello {user.name}, your address {user.addresses.0.street}', {
user: { user: {
name: 'John Doe', name: 'John Doe',
addresses: [ addresses: [
{ street: '123 Main St', city: 'Springfield'}, { street: '123 Main St', city: 'Springfield'},
{ street: '456 Elm St', city: 'Shelbyville'} { street: '456 Elm St', city: 'Shelbyville'},
] ],
} },
}); });