Senior Software Engineer
schummar
marco@schumacher.dev
Augsburg, Munich
pentlandfirth.com
export default {
welcomeMessage: 'Hi, {name}',
currentTime: 'It is now {time, time, short}'
}
<div>
<FormattedMessage
id="myMessage"
defaultMessage="Today is {ts, date, ::yyyyMMdd}"
values={{ts: Date.now()}}
/>
</div>
<div>
<Trans i18nKey="userMessagesUnread" count={count}>
Hello <strong title={t('nameTitle')}>{{name}}</strong>, you have {{count}} unread message.
</Trans>
</div>
<div>
{t('welcomeMessage', { name: 'Marco' })}
</div>
export default {
welcomeMessage: 'Hi, {name}',
currentTime: 'It is now {time, time, short}'
}
<div>
{t('weclomeMesage', { user: 'Marco' })}
</div>
export default {
welcomeMessage: 'Hi, {name}',
currentTime: 'It is now {time, time, short}'
}
const userName: string = 'someone';
const age: number = 32;
const isBanned: boolean = false;
Primitives
Arrays, Tuples
const array: string[] = ['a', 'b', 'c'];
const tuple: [string, number, boolean] = ['a', 1, true];
Functions
function add(x: number, y: number): number {
return x + y;
}
const referenceToAdd: (x: number, y: number) => number = add;
Classes
class Account {
userName: string,
password: string,
}
const account: Account = new Account();
Objects
type User = {
firstName: string,
lastName: string,
}
const enum Direction {
Up,
Down,
Left,
Right,
}
const dir: Direction = Direction.Down;
Enums
Unions
type ClickEvent = { kind: 'click', pos: Position };
type KeyDownEvent = { kind: 'keydown', key: string };
let event: ClickEvent | KeyDownEvent;
if (event.kind === 'click') {
console.log(event.pos);
} else {
console.log(event.key);
}
Intersections
type Animal = {
name: string
}
type Bear = Animal & {
honey: boolean
}
const bear: Bear = getBear();
console.log(bear.name, bear.honey);
Literal types
let message: 'hello';
message = 'hello'; // OK
message = 'world'; // ERROR
type Audience = 'world' | 'Web&Wine';
type Message = `Hello ${Audience}!`
const msg1: Message = 'Hello world!'; // Ok
const msg1: Message = 'Hello Web&Wine!'; // Ok
const msg1: Message = 'Hello Augsburg'; // Error
Template Literal Types
function doSomethingWithUser(user: User) {}
const user = new NotAUser();
doSomethingWithUser(user); // Error
doSomethingWithUser(user as any); // Ok
type Collection<T extends { id: string }> = {
items: T[],
}
type User = {
name: string,
birthday: Date,
}
type Keys = keyof User;
// type Keys = 'name' | 'birthday'
const user = {
name: 'Marco',
birthday: new Date('1988-10-16'),
}
type User = typeof User;
// type User = { name: string, birthday: Date }
type User = {
name: string,
birthday: Date,
}
type V1 = User['name' | 'birthday'];
// type V1 = string | Date
type V2 = User[keyof User];
// type V2 = string | Date
type Example1 = Dog extends Animal ? number : string;
type WithId<T> = T extends { id: any } ? T : T & { id: string };
type T1 = WithId<{ id:number, name: string }>;
// type T1 = { id: number, name: string }
type T2 = WithId<{ name: string }>;
// type T2 = { id: string, name: string }
type UnwrapPromise<T> = T extends Promise<infer S> ? UnwrapPromise<S> : T;
type T1 = UnwrapPromise<string>;
// type T1 = string
type T2 = UnwrapPromise<Promise<string>>;
// type T2 = string
type T3 = UnwrapPromise<Promise<Promise<string>>>;
// type T3 = string
type Partial<T> = {
[K in keyof T]?: T[K]
}
type Book = { title: string, description: string, length: number };
type BookUpdate = Partial<Book>;
// type BookUpdate = { title?: string, description?: string, length?: number }
type StringValues<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K]
};
type Book = { title: string, description: string, length: number };
type BookStringValues = StringValues<Book>;
// type BookStringValues = { title: string, description: string }
Proof: https://gist.github.com/hediet/63f4844acf5ac330804801084f87a6d4
const dict = {
key1: 'value1',
nested: {
key2: 'value2',
key3: 'value3 {param1, plural, one {one} other {many}}'
}
} as const;
type Dict = typeof dict;
type FlatKeys<Dict> = 'key1'
| 'nested.key2'
| 'nested.key3';
type GetICUArgs<'value3 {param, plural, one {one} other {many}}'> = {
param: number
}
type GetICUArgs<'value3 {param1, plural, one {one {param2, date}} other {many}}'> = {
param: number
param2: Date
}
function t<Key extends FlatKeys<Dict>>(key: Key, values: GetICUArgs<DeepValue<Dict, Key>>) {}
type DeepValue<Dict, 'nested.key3'> = 'value3 {param1, plural, one {one} other {many}}'
type Whitespace = ' ' | '\t' | '\n' | '\r';
/** Remove leading and tailing whitespace */
type Trim<T> =
type Whitespace = ' ' | '\t' | '\n' | '\r';
/** Remove leading and tailing whitespace */
type Trim<T> = T extends `${Whitespace}${infer Rest}`
? Trim<Rest>
: T extends `${infer Rest}${Whitespace}`
? Trim<Rest>
: T extends string
? T
: never;
export type FlatKeys<T extends Record<string, unknown>> =
export type FlatKeys<T extends Record<string, unknown>> = string &
keyof {
[K in keyof T as T[K] extends Record<string, unknown> ? `${string & K}.${FlatKeys<T[K]>}` : K]: 1;
};
key1: '...'
nested: {
key2: '...',
key3: '...'
}
key1
`nested.${ }`
key2
key3
'...' extends Record<string, unknown> => No
'...' extends Record<string, unknown> => No
'...' extends Record<string, unknown> => No
{ key2: '...', key3: '...' } extends Record<string, unknown> => Yes
'key2' | 'key3'
export type DeepValue<T extends Record<string, unknown>, K extends string> =
export type DeepValue<T extends Record<string, unknown>, K extends string> =
K extends `${infer Head}.${infer Rest}`
? T[Head] extends Record<string, unknown>
? DeepValue<T[Head], Rest>
: never
: T[K];
Some text { param }
Right
type FindBlocks<Text> =
Text extends `${string}{${infer Right}` //find first {
? ReadBlock<'', Right, ''> extends [infer Block, infer Tail]
? [Block, ...FindBlocks<Tail>] // read block and find next block for tail
: never
: []; // no {, return empty result
type ReadBlock<Block extends string, Tail extends string, Depth extends string> =
Tail extends `${infer L1}}${infer R1}` // find first }
? L1 extends `${infer L2}{${infer R2}` // if preceeded by {, this opens a nested block
? ReadBlock<`${Block}${L2}{`, `${R2}}${R1}`, `${Depth}+`> // then continue search right of this {
: Depth extends `+${infer Rest}` // else if depth > 0
? ReadBlock<`${Block}${L1}}`, R1, Rest> // then finished nested block, continue search right of first }
: [`${Block}${L1}`, R1] // else return full block and search for next
: []; // no }, return emptry result
L1
R1
Result: ['param']
-
-
-
Some text { param, plural, one {1st} }
Right
type FindBlocks<Text> =
Text extends `${string}{${infer Right}` //find first {
? ReadBlock<'', Right, ''> extends [infer Block, infer Tail]
? [Block, ...FindBlocks<Tail>] // read block and find next block for tail
: never
: []; // no {, return empty result
type ReadBlock<Block extends string, Tail extends string, Depth extends string> =
Tail extends `${infer L1}}${infer R1}` // find first }
? L1 extends `${infer L2}{${infer R2}` // if preceeded by {, this opens a nested block
? ReadBlock<`${Block}${L2}{`, `${R2}}${R1}`, `${Depth}+`> // then continue search right of this {
: Depth extends `+${infer Rest}` // else if depth > 0
? ReadBlock<`${Block}${L1}}`, R1, Rest> // then finished nested block, continue search right of first }
: [`${Block}${L1}`, R1] // else return full block and search for next
: []; // no }, return emptry result
L1
R1
L2
R2
=> ReadBlock with
Block = 'param, plural, one {' Tail = '1st}}' Depth = '+'
type FindBlocks<Text> =
Text extends `${string}{${infer Right}` //find first {
? ReadBlock<'', Right, ''> extends [infer Block, infer Tail]
? [Block, ...FindBlocks<Tail>] // read block and find next block for tail
: never
: []; // no {, return empty result
type ReadBlock<Block extends string, Tail extends string, Depth extends string> =
Tail extends `${infer L1}}${infer R1}` // find first }
? L1 extends `${infer L2}{${infer R2}` // if preceeded by {, this opens a nested block
? ReadBlock<`${Block}${L2}{`, `${R2}}${R1}`, `${Depth}+`> // then continue search right of this {
: Depth extends `+${infer Rest}` // else if depth > 0
? ReadBlock<`${Block}${L1}}`, R1, Rest> // then finished nested block, continue search right of first }
: [`${Block}${L1}`, R1] // else return full block and search for next
: []; // no }, return emptry result
Block = 'param, plural, one {' Tail = '1st}}' Depth = '+'
L1
R1
=> ReadBlock with
Block = 'param, plural, one {1st}' Tail = '}' Depth = ''
Text
Text
type FindBlocks<Text> =
Text extends `${string}{${infer Right}` //find first {
? ReadBlock<'', Right, ''> extends [infer Block, infer Tail]
? [Block, ...FindBlocks<Tail>] // read block and find next block for tail
: never
: []; // no {, return empty result
type ReadBlock<Block extends string, Tail extends string, Depth extends string> =
Tail extends `${infer L1}}${infer R1}` // find first }
? L1 extends `${infer L2}{${infer R2}` // if preceeded by {, this opens a nested block
? ReadBlock<`${Block}${L2}{`, `${R2}}${R1}`, `${Depth}+`> // then continue search right of this {
: Depth extends `+${infer Rest}` // else if depth > 0
? ReadBlock<`${Block}${L1}}`, R1, Rest> // then finished nested block, continue search right of first }
: [`${Block}${L1}`, R1] // else return full block and search for next
: []; // no }, return emptry result
Block = 'param, plural, one {1st}' Tail = '}' Depth = ''
L1
R1
Result: ['param, plural, one {1st}']
Text
Text
Text
type ParseBlock<Block> = Block extends `${infer Name},${infer Format},${infer Rest}`
? { [K in Trim<Name>]: VariableType<Trim<Format>> } & TupleParseBlock<TupleFindBlocks<FindBlocks<Rest>>>
: Block extends `${infer Name},${infer Format}`
? { [K in Trim<Name>]: VariableType<Trim<Format>> }
: { [K in Trim<Block>]: Value };
type VariableType<T extends string> = T extends 'number' | 'plural' | 'selectordinal' ? number
: T extends 'date' | 'time' ? Date
: Value;
export type GetICUArgs<T> = T extends string ? TupleParseBlock<FindBlocks<T>> : never;
'param' => { param: Value }
'param, plural, one { 1st {nested, Date} }' => {}
'param, plural, one { 1st {nested, Date} }' => { param: }
'param, plural, one { 1st {nested, Date} }' => { param: number }
'param, plural, one { 1st {nested, Date} }' => { param: number } & {}
'param, plural, one { 1st {nested, Date} }' => { param: number } & { nested: }
'param, plural, one { 1st {nested, Date} }' => { param: number } & { nested: Date }
DEMO
schummar/schummar-translate
npm schummar-translate