@discord/intl 0.1.0-rc.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +29 -0
- package/dist/intl-manager.d.ts +18 -8
- package/dist/react.d.ts +21 -6
- package/dist/react.js +1 -1
- package/dist/types.d.ts +171 -9
- package/package.json +1 -1
- package/src/index.ts +32 -0
- package/src/intl-manager.ts +35 -21
- package/src/react.ts +42 -19
- package/src/types.d.ts +171 -9
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
import { IntlManager } from './intl-manager';
|
|
2
|
+
import { IntlManagerReact } from './react';
|
|
1
3
|
export { IntlManager, createLoader, DEFAULT_LOCALE } from './intl-manager';
|
|
2
4
|
export { IntlManagerReact } from './react';
|
|
3
5
|
export type * from './types.d.ts';
|
|
6
|
+
/**
|
|
7
|
+
* A type representing any message that has been formatted using one of the
|
|
8
|
+
* default formatting methods in the `@discord/intl` system. Use this type
|
|
9
|
+
* when you don't care about whether a message is static or contains rich text
|
|
10
|
+
* or has dynamically-formatted values, when you need to handle both in tandem,
|
|
11
|
+
* or when you need a generic return type for a function returning "any
|
|
12
|
+
* any-already formatted message".
|
|
13
|
+
*
|
|
14
|
+
* Generally, this is like `React.ReactNode`, accepting both strings and rich
|
|
15
|
+
* `ReactElement`s, but it is more specific to disallow nullish values and
|
|
16
|
+
* other special React elements like `ReactPortal`. Seeing this type, you can
|
|
17
|
+
* be confident that the value is intended to be the result of formatting an
|
|
18
|
+
* intl message, even if the actual value comes from elsewhere (like a
|
|
19
|
+
* user-generated string).
|
|
20
|
+
*/
|
|
21
|
+
export type ReactIntlMessage = string | ReturnType<IntlManagerReact<any>['format']>;
|
|
22
|
+
/**
|
|
23
|
+
* The return value of `formatToParts` from `@discord/intl`, this type
|
|
24
|
+
* represents any AST structure for a message rendered using this system.
|
|
25
|
+
* The AST generally follows a Markdown-like structure, with text nodes
|
|
26
|
+
* interspersed within and around rich text formatting nodes.
|
|
27
|
+
*
|
|
28
|
+
* ASTs are created _after_ all placeholders have been filled by values
|
|
29
|
+
* from a call to `formatToParts`, meaning they are intended to be fully static
|
|
30
|
+
* structures passed around for custom rendering functions to use.
|
|
31
|
+
*/
|
|
32
|
+
export type IntlMessageAst = ReturnType<IntlManager<any, any>['formatToParts']>;
|
package/dist/intl-manager.d.ts
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import { IntlShape } from '@formatjs/intl';
|
|
2
2
|
import { LocaleId, LocaleImportMap, MessageLoader } from './message-loader';
|
|
3
|
-
import type {
|
|
3
|
+
import type { FunctionTypeMap, IntlMessageGetter, RequiredFormatValues, RichTextElementMap, TypedIntlMessageGetter } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Types for formatting functions when calling `formatToParts`, ensuring the
|
|
6
|
+
* functions yield value AST nodes.
|
|
7
|
+
*/
|
|
8
|
+
type AstFunctionTypes = {
|
|
9
|
+
link: undefined | object | ((children: object) => Array<string | any>);
|
|
10
|
+
hook: undefined | object | ((children: object) => Array<string | any>);
|
|
11
|
+
handler: undefined | object | ((children: object) => Array<string | any>);
|
|
12
|
+
};
|
|
4
13
|
/**
|
|
5
14
|
* Fallback locale used for all internationalization when an operation in the
|
|
6
15
|
* requested locale is not possible.
|
|
7
16
|
*/
|
|
8
17
|
export declare const DEFAULT_LOCALE: string;
|
|
9
|
-
export declare class IntlManager<DefaultElements extends RichTextElementMap,
|
|
18
|
+
export declare class IntlManager<DefaultElements extends RichTextElementMap, FunctionTypes extends FunctionTypeMap> {
|
|
10
19
|
defaultLocale: string;
|
|
11
20
|
currentLocale: string;
|
|
12
21
|
intl: IntlShape;
|
|
@@ -42,8 +51,8 @@ export declare class IntlManager<DefaultElements extends RichTextElementMap, Def
|
|
|
42
51
|
* use `format`, which will wrap the formatting in a React component that
|
|
43
52
|
* subscribes to the current locale and state of loaded messages.
|
|
44
53
|
*/
|
|
45
|
-
formatToParts<T extends TypedIntlMessageGetter<
|
|
46
|
-
formatToParts<T extends
|
|
54
|
+
formatToParts<T extends TypedIntlMessageGetter<undefined>>(message: T): Array<string | any>;
|
|
55
|
+
formatToParts<T extends IntlMessageGetter>(message: T, values: RequiredFormatValues<T, DefaultElements, AstFunctionTypes>): Array<string | any>;
|
|
47
56
|
/**
|
|
48
57
|
* For messages with no formatting values (i.e., plain strings, they can skip
|
|
49
58
|
* a majority of the processing work and just resolve the message itself for
|
|
@@ -58,8 +67,8 @@ export declare class IntlManager<DefaultElements extends RichTextElementMap, Def
|
|
|
58
67
|
* Format the given message with the provided values, removing any styling
|
|
59
68
|
* and non-textual content from the message, returning a plain string.
|
|
60
69
|
*/
|
|
61
|
-
formatToPlainString<T extends TypedIntlMessageGetter<
|
|
62
|
-
formatToPlainString<T extends
|
|
70
|
+
formatToPlainString<T extends TypedIntlMessageGetter<undefined>>(message: T): string;
|
|
71
|
+
formatToPlainString<T extends IntlMessageGetter>(message: T, values: RequiredFormatValues<T, DefaultElements, FunctionTypes>): string;
|
|
63
72
|
/**
|
|
64
73
|
* Similar to `formatToPlainString`, format the given message with the provided
|
|
65
74
|
* values, but convert all rich text formatting back to Markdown syntax rather
|
|
@@ -67,8 +76,8 @@ export declare class IntlManager<DefaultElements extends RichTextElementMap, Def
|
|
|
67
76
|
* can be sent through a separate Markdown renderer to get an equivalent
|
|
68
77
|
* result to formatting this message directly.
|
|
69
78
|
*/
|
|
70
|
-
formatToMarkdownString<T extends TypedIntlMessageGetter<
|
|
71
|
-
formatToMarkdownString<T extends
|
|
79
|
+
formatToMarkdownString<T extends TypedIntlMessageGetter<undefined>>(message: T): string;
|
|
80
|
+
formatToMarkdownString<T extends IntlMessageGetter>(message: T, values: RequiredFormatValues<T, DefaultElements, FunctionTypes>): string;
|
|
72
81
|
}
|
|
73
82
|
/**
|
|
74
83
|
* Create a new MessageLoader, which handles lazily loading messages for
|
|
@@ -76,3 +85,4 @@ export declare class IntlManager<DefaultElements extends RichTextElementMap, Def
|
|
|
76
85
|
* message defined in `messageKeys`.
|
|
77
86
|
*/
|
|
78
87
|
export declare function createLoader(messageKeys: string[], localeImportMap: LocaleImportMap, defaultLocale: LocaleId): MessageLoader;
|
|
88
|
+
export {};
|
package/dist/react.d.ts
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { IntlManager } from './intl-manager';
|
|
3
|
-
import type {
|
|
4
|
-
|
|
3
|
+
import type { IntlMessageGetter, RequiredFormatValues, RichTextElementMap, TypedIntlMessageGetter } from './types';
|
|
4
|
+
type ReactHandlerEvent = React.MouseEvent | React.KeyboardEvent;
|
|
5
|
+
type ReactClickHandler = (e: ReactHandlerEvent) => void;
|
|
6
|
+
type ReactFunctionTypes = {
|
|
7
|
+
link: undefined | ((content: React.ReactNode | React.ReactNode[], key: string) => React.ReactNode);
|
|
8
|
+
hook: undefined | ((content: React.ReactNode | React.ReactNode[], key: string) => React.ReactNode);
|
|
9
|
+
handler: undefined | ReactClickHandler | {
|
|
10
|
+
onClick: ReactClickHandler;
|
|
11
|
+
} | {
|
|
12
|
+
onContextMenu: ReactClickHandler;
|
|
13
|
+
} | {
|
|
14
|
+
onClick: ReactClickHandler;
|
|
15
|
+
onContextMenu: ReactClickHandler;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export declare class IntlManagerReact<DefaultElements extends RichTextElementMap> extends IntlManager<DefaultElements, ReactFunctionTypes> {
|
|
5
19
|
/**
|
|
6
20
|
* A reactive component form of `intl.format` that automatically updates when
|
|
7
21
|
* the application's locale changes and when new data is loaded for the
|
|
8
22
|
* subject message.
|
|
9
23
|
*/
|
|
10
|
-
IntlMessage: <T extends
|
|
24
|
+
IntlMessage: <T extends IntlMessageGetter>(props: {
|
|
11
25
|
message: T;
|
|
12
|
-
values?:
|
|
26
|
+
values?: RequiredFormatValues<T, DefaultElements, ReactFunctionTypes> | never;
|
|
13
27
|
}) => string | React.FunctionComponentElement<{
|
|
14
28
|
children?: React.ReactNode | undefined;
|
|
15
29
|
}>;
|
|
@@ -18,6 +32,7 @@ export declare class IntlManagerReact<DefaultElements extends RichTextElementMap
|
|
|
18
32
|
* and respond to updates about the current locale and other relevant
|
|
19
33
|
* information.
|
|
20
34
|
*/
|
|
21
|
-
format<T extends TypedIntlMessageGetter<
|
|
22
|
-
format<T extends
|
|
35
|
+
format<T extends TypedIntlMessageGetter<undefined>>(message: T): React.ReactElement;
|
|
36
|
+
format<T extends IntlMessageGetter>(message: T, values: RequiredFormatValues<T, DefaultElements, ReactFunctionTypes>): React.ReactElement;
|
|
23
37
|
}
|
|
38
|
+
export {};
|
package/dist/react.js
CHANGED
|
@@ -57,7 +57,7 @@ var IntlManagerReact = /** @class */ (function (_super) {
|
|
|
57
57
|
// Use the locale from this point in the application, which may be
|
|
58
58
|
// different from the global locale.
|
|
59
59
|
var locale = React.useSyncExternalStore(_this.onLocaleChange, function () { return _this.currentLocale; });
|
|
60
|
-
// TODO(faulty): This can and should be replaced
|
|
60
|
+
// TODO(faulty): This can and should be replaced with
|
|
61
61
|
// `use(messagesLoadedPromise)` once `use` is shipped to stable.
|
|
62
62
|
// Source the actual message to render for that locale from its loader.
|
|
63
63
|
React.useSyncExternalStore(message.onChange, function () { return message(locale); });
|
package/dist/types.d.ts
CHANGED
|
@@ -19,6 +19,28 @@ export declare function defineMessages(messages: object): object;
|
|
|
19
19
|
*/
|
|
20
20
|
export type RichTextElementMap = Record<string, (chunks: any[]) => any>;
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Placeholder type representing that this property is a Hook to be rendered
|
|
24
|
+
* while formatting.
|
|
25
|
+
*/
|
|
26
|
+
export type HookFunction = { __brand: 'hook-function' };
|
|
27
|
+
/**
|
|
28
|
+
* Placeholder type representing that this property is a Link to be rendered
|
|
29
|
+
* while formatting.
|
|
30
|
+
*/
|
|
31
|
+
export type LinkFunction = { __brand: 'link-function' };
|
|
32
|
+
/**
|
|
33
|
+
* Placeholder type representing that this property is a Handler function,
|
|
34
|
+
* which will be attached to some event when the message is formatted.
|
|
35
|
+
*/
|
|
36
|
+
export type HandlerFunction = { __brand: 'handler-function' };
|
|
37
|
+
|
|
38
|
+
export type IntlNumber = string | number | null | undefined;
|
|
39
|
+
export type IntlPlural = string | number | null | undefined;
|
|
40
|
+
export type IntlAny = any;
|
|
41
|
+
export type IntlDate = string | number | Date | null | undefined;
|
|
42
|
+
export type IntlTime = string | number | Date | null | undefined;
|
|
43
|
+
|
|
22
44
|
/**
|
|
23
45
|
* Messages with no rich formatting and no value interpolations are just plain
|
|
24
46
|
* strings, and are able to skip multiple formatting steps to work more
|
|
@@ -32,12 +54,16 @@ type PlainIntlMessage = string;
|
|
|
32
54
|
* the message should be typed as a `PlainIntlMessage`.
|
|
33
55
|
*/
|
|
34
56
|
type TypedIntlMessage<FormatValues extends object> = InternalIntlMessage & {
|
|
35
|
-
|
|
57
|
+
__values: FormatValues;
|
|
36
58
|
};
|
|
37
59
|
|
|
38
60
|
type AnyIntlMessage<FormatValues extends object | undefined = undefined> =
|
|
39
61
|
FormatValues extends undefined ? PlainIntlMessage : TypedIntlMessage<FormatValues>;
|
|
40
62
|
|
|
63
|
+
export interface IntlMessageGetter extends IntlMessageGetterAdditions {
|
|
64
|
+
(locale: string): InternalIntlMessage | PlainIntlMessage;
|
|
65
|
+
}
|
|
66
|
+
|
|
41
67
|
/**
|
|
42
68
|
* Getter function that retrieves a message that best matches the requested
|
|
43
69
|
* locale. `FormatValues` represents either an object containing all of the
|
|
@@ -47,7 +73,50 @@ type AnyIntlMessage<FormatValues extends object | undefined = undefined> =
|
|
|
47
73
|
*/
|
|
48
74
|
export interface TypedIntlMessageGetter<FormatValues extends object | undefined>
|
|
49
75
|
extends IntlMessageGetterAdditions {
|
|
50
|
-
|
|
76
|
+
// TODO: This is lossy and unfortunate that typing can't be propagated
|
|
77
|
+
// to the returned message type, but doing so causes problems with
|
|
78
|
+
// contravariance of the type. When returning a message getter from a
|
|
79
|
+
// function with a generic message getter return type, TypeScript will
|
|
80
|
+
// try to assert that every returned type is strictly compatible with the
|
|
81
|
+
// declared return type. In that case, if a message is missing any of the
|
|
82
|
+
// properties declared in the type, it will error. But in the other direction
|
|
83
|
+
// when passing a message getter as a parameter _into_ a function, TypeScript
|
|
84
|
+
// will do the opposite and error if the getter has any _extra_ properties
|
|
85
|
+
// from the declared type. The latter of these is definitely semantically
|
|
86
|
+
// correct, but the former is more just an annoying part of the type system
|
|
87
|
+
// because of this returned type being nested within this interface and the
|
|
88
|
+
// strict subtying that TypeScript uses.
|
|
89
|
+
(locale: string): InternalIntlMessage | PlainIntlMessage;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* A phantom type (not part of the actual typing of the object) that ensures
|
|
93
|
+
* TypeScript won't use subtyping to widen the types of multiple messages
|
|
94
|
+
* unless they are _identical_. For an example of the problem:
|
|
95
|
+
* ```typescript
|
|
96
|
+
* declare const message1: {a: string};
|
|
97
|
+
* declare const message2: {a: string, b: boolean};
|
|
98
|
+
*
|
|
99
|
+
* const foo = condition ? message1 : message2;
|
|
100
|
+
* // ^ This becomes just `{a: string}`, because it's the common base type.
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* This is fine for representing the object itself, but on messages those
|
|
104
|
+
* types represent required values at runtime, and so the greatest outer
|
|
105
|
+
* type is the only valid answer, rather than the greatest subtype.
|
|
106
|
+
*
|
|
107
|
+
* This phantom property ensures that TypeScript will never consider two
|
|
108
|
+
* messages subtypes of each other unless they contain identical properties,
|
|
109
|
+
* even the values of those properties, despite the fact that this only
|
|
110
|
+
* considers names being passed in (not sure how, but it does). The result
|
|
111
|
+
* is then always a union type of the possible types, which further
|
|
112
|
+
* downstream will ensure that all format values are always checked for all
|
|
113
|
+
* possible cases of the message.
|
|
114
|
+
*/
|
|
115
|
+
__phantom: object extends FormatValues
|
|
116
|
+
? any
|
|
117
|
+
: FormatValues extends undefined
|
|
118
|
+
? any
|
|
119
|
+
: keyof FormatValues;
|
|
51
120
|
}
|
|
52
121
|
|
|
53
122
|
/**
|
|
@@ -122,16 +191,109 @@ type FormatValuesFor<T> =
|
|
|
122
191
|
// be supplied, represented by the `never`. This prevents accidentally
|
|
123
192
|
// passing an object, even an empty one, for those strings, and allows the
|
|
124
193
|
// formatter to optimize a little more on each call.
|
|
125
|
-
[T] extends [
|
|
194
|
+
[T] extends [undefined]
|
|
126
195
|
? never
|
|
127
196
|
: // The `never` condition needs to be repeated here, but this time _without_
|
|
128
197
|
// the `[]` syntax, so that only the union elements with actual values are
|
|
129
198
|
// included in the union. Without this, the `infer U` on the latter side
|
|
130
199
|
// fails to resolve and the type just becomes `never`.
|
|
131
|
-
UnionToIntersection<
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
200
|
+
UnionToIntersection<T extends undefined ? never : T>;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* A template type for replaceable placeholder types to be defined by formatter
|
|
204
|
+
* implementations. Messages are typed using placeholder types like
|
|
205
|
+
* `HookFunction` and `HandlerFunction`, which are empty branded types
|
|
206
|
+
* indicating the _intent_ of a property, which the formatter is then able to
|
|
207
|
+
* replace with a more appropriate type. For example, a React formatter might
|
|
208
|
+
* replace a `HookFunction` with `(content: ReactNode) => ReactNode` to allow
|
|
209
|
+
* the caller to inject React elements within a string, while an AST formatter
|
|
210
|
+
* might require a specific object shape, or allow any plain object.
|
|
211
|
+
*/
|
|
212
|
+
interface FunctionTypeMap {
|
|
213
|
+
link: any;
|
|
214
|
+
hook: any;
|
|
215
|
+
handler: any;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
type ReplaceKeepingNullability<T, P> = P | Exclude<T, NonNullable<T>>;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Type implementation for applying a `FunctionTypeMap` to a given argument
|
|
222
|
+
* type `T`. For each property of `T`, if its value is one of the placeholder
|
|
223
|
+
* types, like `HookFunction`, it will be replaced by the corresponding
|
|
224
|
+
* template value from `FunctionTypes`.
|
|
225
|
+
*/
|
|
226
|
+
type MapFunctionTypes<T, FunctionTypes extends FunctionTypeMap> = {
|
|
227
|
+
[K in keyof T]: ReplaceKeepingNullability<
|
|
228
|
+
T[K],
|
|
229
|
+
NonNullable<T[K]> extends LinkFunction
|
|
230
|
+
? FunctionTypes['link']
|
|
231
|
+
: NonNullable<T[K]> extends HookFunction
|
|
232
|
+
? FunctionTypes['hook']
|
|
233
|
+
: NonNullable<T[K]> extends HandlerFunction
|
|
234
|
+
? FunctionTypes['handler']
|
|
235
|
+
: T[K]
|
|
236
|
+
>;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// Detect if T is explicitly `any`, in which case we don't want to do
|
|
240
|
+
// any replacements, since each type should be `any`.
|
|
241
|
+
// https://stackoverflow.com/a/61625831
|
|
242
|
+
type CheckStrictAny<Check, Yes, No> = (Check extends never ? true : false) extends false ? No : Yes;
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Given a `TypedIntlMessageGetter` type, return the format values type
|
|
246
|
+
* argument from it. For example:
|
|
247
|
+
*
|
|
248
|
+
* ```typescript
|
|
249
|
+
* type SomeMessage = TypedIntlMessageGetter<{name: string}>
|
|
250
|
+
* type Result = IntlMessageGetterInnerType<SomeMessage>
|
|
251
|
+
* // Result is `{name: string}`
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
type IntlMessageGetterInnerType<T extends IntlMessageGetter> =
|
|
255
|
+
T extends TypedIntlMessageGetter<infer U> ? U : never;
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Create the format values argument type for a format function using the
|
|
259
|
+
* configured `DefaultElements` for a formatter and injecting the placeholder
|
|
260
|
+
* types from `FunctionTypes`. The result is the set of required values
|
|
261
|
+
* necessary to format the message of type `T` with the calling formatter.
|
|
262
|
+
*/
|
|
263
|
+
type RequiredFormatValues<
|
|
264
|
+
Values extends IntlMessageGetter,
|
|
265
|
+
DefaultElements,
|
|
266
|
+
FunctionTypes extends FunctionTypeMap,
|
|
267
|
+
> =
|
|
268
|
+
IntlMessageGetterInnerType<Values> extends undefined
|
|
269
|
+
? never
|
|
270
|
+
: CheckStrictAny<
|
|
271
|
+
IntlMessageGetterInnerType<Values>,
|
|
272
|
+
// If `T` is explicitly `any`, then individual properties can't be typed and
|
|
273
|
+
// anything is allowed. This is only typical when consumers manually create a
|
|
274
|
+
// `TypedIntlMessageGetter` type as needed to pass values around, but loses
|
|
275
|
+
// any real type safety for the formatted values.
|
|
276
|
+
// Without this, though, `MapFunctionTypes` would always match the first
|
|
277
|
+
// value and turn every property into the mapped `LinkFunction` type, which
|
|
278
|
+
// is almost always going to be wrong.
|
|
279
|
+
any,
|
|
280
|
+
MapFunctionTypes<
|
|
281
|
+
Omit<FormatValuesFor<IntlMessageGetterInnerType<Values>>, keyof DefaultElements>,
|
|
282
|
+
FunctionTypes
|
|
283
|
+
>
|
|
137
284
|
>;
|
|
285
|
+
|
|
286
|
+
export interface IntlOptionalRichTextKeys {
|
|
287
|
+
$_: any;
|
|
288
|
+
$b: any;
|
|
289
|
+
$i: any;
|
|
290
|
+
$p: any;
|
|
291
|
+
$link: any;
|
|
292
|
+
$code: any;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export type IntlFormatFunctionValuesArg<T extends IntlMessageGetter> = RequiredFormatValues<
|
|
296
|
+
T,
|
|
297
|
+
IntlOptionalRichTextKeys,
|
|
298
|
+
FunctionTypeMap
|
|
299
|
+
>;
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,4 +1,36 @@
|
|
|
1
|
+
import { IntlManager } from './intl-manager';
|
|
2
|
+
import { IntlManagerReact } from './react';
|
|
3
|
+
|
|
1
4
|
export { IntlManager, createLoader, DEFAULT_LOCALE } from './intl-manager';
|
|
2
5
|
export { IntlManagerReact } from './react';
|
|
3
6
|
|
|
4
7
|
export type * from './types.d.ts';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A type representing any message that has been formatted using one of the
|
|
11
|
+
* default formatting methods in the `@discord/intl` system. Use this type
|
|
12
|
+
* when you don't care about whether a message is static or contains rich text
|
|
13
|
+
* or has dynamically-formatted values, when you need to handle both in tandem,
|
|
14
|
+
* or when you need a generic return type for a function returning "any
|
|
15
|
+
* any-already formatted message".
|
|
16
|
+
*
|
|
17
|
+
* Generally, this is like `React.ReactNode`, accepting both strings and rich
|
|
18
|
+
* `ReactElement`s, but it is more specific to disallow nullish values and
|
|
19
|
+
* other special React elements like `ReactPortal`. Seeing this type, you can
|
|
20
|
+
* be confident that the value is intended to be the result of formatting an
|
|
21
|
+
* intl message, even if the actual value comes from elsewhere (like a
|
|
22
|
+
* user-generated string).
|
|
23
|
+
*/
|
|
24
|
+
export type ReactIntlMessage = string | ReturnType<IntlManagerReact<any>['format']>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The return value of `formatToParts` from `@discord/intl`, this type
|
|
28
|
+
* represents any AST structure for a message rendered using this system.
|
|
29
|
+
* The AST generally follows a Markdown-like structure, with text nodes
|
|
30
|
+
* interspersed within and around rich text formatting nodes.
|
|
31
|
+
*
|
|
32
|
+
* ASTs are created _after_ all placeholders have been filled by values
|
|
33
|
+
* from a call to `formatToParts`, meaning they are intended to be fully static
|
|
34
|
+
* structures passed around for custom rendering functions to use.
|
|
35
|
+
*/
|
|
36
|
+
export type IntlMessageAst = ReturnType<IntlManager<any, any>['formatToParts']>;
|
package/src/intl-manager.ts
CHANGED
|
@@ -8,7 +8,23 @@ import {
|
|
|
8
8
|
|
|
9
9
|
import { LocaleId, LocaleImportMap, MessageLoader } from './message-loader';
|
|
10
10
|
|
|
11
|
-
import type {
|
|
11
|
+
import type {
|
|
12
|
+
FunctionTypeMap,
|
|
13
|
+
IntlMessageGetter,
|
|
14
|
+
RequiredFormatValues,
|
|
15
|
+
RichTextElementMap,
|
|
16
|
+
TypedIntlMessageGetter,
|
|
17
|
+
} from './types';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Types for formatting functions when calling `formatToParts`, ensuring the
|
|
21
|
+
* functions yield value AST nodes.
|
|
22
|
+
*/
|
|
23
|
+
type AstFunctionTypes = {
|
|
24
|
+
link: undefined | object | ((children: object) => Array<string | any>);
|
|
25
|
+
hook: undefined | object | ((children: object) => Array<string | any>);
|
|
26
|
+
handler: undefined | object | ((children: object) => Array<string | any>);
|
|
27
|
+
};
|
|
12
28
|
|
|
13
29
|
/**
|
|
14
30
|
* Fallback locale used for all internationalization when an operation in the
|
|
@@ -18,7 +34,7 @@ export const DEFAULT_LOCALE: string = 'en-US';
|
|
|
18
34
|
|
|
19
35
|
export class IntlManager<
|
|
20
36
|
DefaultElements extends RichTextElementMap,
|
|
21
|
-
|
|
37
|
+
FunctionTypes extends FunctionTypeMap,
|
|
22
38
|
> {
|
|
23
39
|
defaultLocale: string;
|
|
24
40
|
currentLocale: string;
|
|
@@ -84,16 +100,14 @@ export class IntlManager<
|
|
|
84
100
|
* use `format`, which will wrap the formatting in a React component that
|
|
85
101
|
* subscribes to the current locale and state of loaded messages.
|
|
86
102
|
*/
|
|
87
|
-
formatToParts<T extends TypedIntlMessageGetter<
|
|
88
|
-
|
|
89
|
-
): Array<string | any>;
|
|
90
|
-
formatToParts<T extends TypedIntlMessageGetter<object | undefined>>(
|
|
103
|
+
formatToParts<T extends TypedIntlMessageGetter<undefined>>(message: T): Array<string | any>;
|
|
104
|
+
formatToParts<T extends IntlMessageGetter>(
|
|
91
105
|
message: T,
|
|
92
|
-
values:
|
|
106
|
+
values: RequiredFormatValues<T, DefaultElements, AstFunctionTypes>,
|
|
93
107
|
): Array<string | any>;
|
|
94
|
-
formatToParts<T extends
|
|
108
|
+
formatToParts<T extends IntlMessageGetter>(
|
|
95
109
|
message: T,
|
|
96
|
-
values?:
|
|
110
|
+
values?: RequiredFormatValues<T, DefaultElements, AstFunctionTypes>,
|
|
97
111
|
): Array<string | any> {
|
|
98
112
|
if (typeof message === 'string') return [message];
|
|
99
113
|
const resolvedMessage = typeof message === 'function' ? message(this.currentLocale) : message;
|
|
@@ -147,15 +161,15 @@ export class IntlManager<
|
|
|
147
161
|
* Format the given message with the provided values, removing any styling
|
|
148
162
|
* and non-textual content from the message, returning a plain string.
|
|
149
163
|
*/
|
|
150
|
-
formatToPlainString<T extends TypedIntlMessageGetter<
|
|
151
|
-
formatToPlainString<T extends
|
|
164
|
+
formatToPlainString<T extends TypedIntlMessageGetter<undefined>>(message: T): string;
|
|
165
|
+
formatToPlainString<T extends IntlMessageGetter>(
|
|
152
166
|
message: T,
|
|
153
|
-
values:
|
|
167
|
+
values: RequiredFormatValues<T, DefaultElements, FunctionTypes>,
|
|
154
168
|
): string;
|
|
155
|
-
formatToPlainString<T extends
|
|
169
|
+
formatToPlainString<T extends IntlMessageGetter>(
|
|
156
170
|
message: T,
|
|
157
|
-
values?:
|
|
158
|
-
) {
|
|
171
|
+
values?: RequiredFormatValues<T, DefaultElements, FunctionTypes>,
|
|
172
|
+
): string {
|
|
159
173
|
if (typeof message === 'string') return message;
|
|
160
174
|
const resolvedMessage = message(this.currentLocale);
|
|
161
175
|
if (typeof resolvedMessage === 'string') return resolvedMessage;
|
|
@@ -176,15 +190,15 @@ export class IntlManager<
|
|
|
176
190
|
* can be sent through a separate Markdown renderer to get an equivalent
|
|
177
191
|
* result to formatting this message directly.
|
|
178
192
|
*/
|
|
179
|
-
formatToMarkdownString<T extends TypedIntlMessageGetter<
|
|
180
|
-
formatToMarkdownString<T extends
|
|
193
|
+
formatToMarkdownString<T extends TypedIntlMessageGetter<undefined>>(message: T): string;
|
|
194
|
+
formatToMarkdownString<T extends IntlMessageGetter>(
|
|
181
195
|
message: T,
|
|
182
|
-
values:
|
|
196
|
+
values: RequiredFormatValues<T, DefaultElements, FunctionTypes>,
|
|
183
197
|
): string;
|
|
184
|
-
formatToMarkdownString<T extends
|
|
198
|
+
formatToMarkdownString<T extends IntlMessageGetter>(
|
|
185
199
|
message: T,
|
|
186
|
-
values?:
|
|
187
|
-
) {
|
|
200
|
+
values?: RequiredFormatValues<T, DefaultElements, FunctionTypes>,
|
|
201
|
+
): string {
|
|
188
202
|
// TODO(faulty): Implement the markdown syntax conversion here.
|
|
189
203
|
if (typeof message === 'string') return message;
|
|
190
204
|
const resolvedMessage = message(this.currentLocale);
|
package/src/react.ts
CHANGED
|
@@ -2,26 +2,53 @@ import * as React from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { IntlManager } from './intl-manager';
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
IntlMessageGetter,
|
|
7
|
+
RequiredFormatValues,
|
|
8
|
+
RichTextElementMap,
|
|
9
|
+
TypedIntlMessageGetter,
|
|
10
|
+
} from './types';
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
type ReactHandlerEvent = React.MouseEvent | React.KeyboardEvent;
|
|
13
|
+
type ReactClickHandler = (e: ReactHandlerEvent) => void;
|
|
14
|
+
|
|
15
|
+
type ReactFunctionTypes = {
|
|
16
|
+
// TODO: Allowing `undefined` on these is a migration artifact that should be
|
|
17
|
+
// removed. Functions, like all other values, should be required when
|
|
18
|
+
// formatting to avoid unexpected missing behavior.
|
|
19
|
+
link:
|
|
20
|
+
| undefined
|
|
21
|
+
| ((content: React.ReactNode | React.ReactNode[], key: string) => React.ReactNode);
|
|
22
|
+
|
|
23
|
+
hook:
|
|
24
|
+
| undefined
|
|
25
|
+
| ((content: React.ReactNode | React.ReactNode[], key: string) => React.ReactNode);
|
|
26
|
+
handler:
|
|
27
|
+
| undefined
|
|
28
|
+
| ReactClickHandler
|
|
29
|
+
| { onClick: ReactClickHandler }
|
|
30
|
+
| { onContextMenu: ReactClickHandler }
|
|
31
|
+
| { onClick: ReactClickHandler; onContextMenu: ReactClickHandler };
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export class IntlManagerReact<DefaultElements extends RichTextElementMap> extends IntlManager<
|
|
35
|
+
DefaultElements,
|
|
36
|
+
ReactFunctionTypes
|
|
37
|
+
> {
|
|
11
38
|
/**
|
|
12
39
|
* A reactive component form of `intl.format` that automatically updates when
|
|
13
40
|
* the application's locale changes and when new data is loaded for the
|
|
14
41
|
* subject message.
|
|
15
42
|
*/
|
|
16
|
-
IntlMessage = <T extends
|
|
43
|
+
IntlMessage = <T extends IntlMessageGetter>(props: {
|
|
17
44
|
message: T;
|
|
18
|
-
values?:
|
|
45
|
+
values?: RequiredFormatValues<T, DefaultElements, ReactFunctionTypes> | never;
|
|
19
46
|
}) => {
|
|
20
47
|
const { message, values } = props;
|
|
21
48
|
// Use the locale from this point in the application, which may be
|
|
22
49
|
// different from the global locale.
|
|
23
50
|
const locale = React.useSyncExternalStore(this.onLocaleChange, () => this.currentLocale);
|
|
24
|
-
// TODO(faulty): This can and should be replaced
|
|
51
|
+
// TODO(faulty): This can and should be replaced with
|
|
25
52
|
// `use(messagesLoadedPromise)` once `use` is shipped to stable.
|
|
26
53
|
// Source the actual message to render for that locale from its loader.
|
|
27
54
|
React.useSyncExternalStore(message.onChange, () => message(locale));
|
|
@@ -29,13 +56,9 @@ export class IntlManagerReact<
|
|
|
29
56
|
// a plain string.
|
|
30
57
|
if (typeof message === 'string') return message;
|
|
31
58
|
|
|
32
|
-
const parts = this.formatToParts(message, values
|
|
59
|
+
const parts = this.formatToParts(message, values);
|
|
33
60
|
if (parts.length === 1 && typeof parts[0] === 'string') return parts[0];
|
|
34
|
-
return React.createElement(
|
|
35
|
-
React.Fragment,
|
|
36
|
-
undefined,
|
|
37
|
-
this.formatToParts(message, values as Omit<FormatValuesFor<T>, DefaultValues>),
|
|
38
|
-
);
|
|
61
|
+
return React.createElement(React.Fragment, undefined, this.formatToParts(message, values));
|
|
39
62
|
};
|
|
40
63
|
|
|
41
64
|
/**
|
|
@@ -43,14 +66,14 @@ export class IntlManagerReact<
|
|
|
43
66
|
* and respond to updates about the current locale and other relevant
|
|
44
67
|
* information.
|
|
45
68
|
*/
|
|
46
|
-
format<T extends TypedIntlMessageGetter<
|
|
47
|
-
format<T extends
|
|
69
|
+
format<T extends TypedIntlMessageGetter<undefined>>(message: T): React.ReactElement;
|
|
70
|
+
format<T extends IntlMessageGetter>(
|
|
48
71
|
message: T,
|
|
49
|
-
values:
|
|
72
|
+
values: RequiredFormatValues<T, DefaultElements, ReactFunctionTypes>,
|
|
50
73
|
): React.ReactElement;
|
|
51
|
-
format<T extends
|
|
74
|
+
format<T extends IntlMessageGetter>(
|
|
52
75
|
message: T,
|
|
53
|
-
values?:
|
|
76
|
+
values?: RequiredFormatValues<T, DefaultElements, ReactFunctionTypes>,
|
|
54
77
|
): React.ReactElement {
|
|
55
78
|
return React.createElement(this.IntlMessage<T>, { message, values });
|
|
56
79
|
}
|
package/src/types.d.ts
CHANGED
|
@@ -19,6 +19,28 @@ export declare function defineMessages(messages: object): object;
|
|
|
19
19
|
*/
|
|
20
20
|
export type RichTextElementMap = Record<string, (chunks: any[]) => any>;
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Placeholder type representing that this property is a Hook to be rendered
|
|
24
|
+
* while formatting.
|
|
25
|
+
*/
|
|
26
|
+
export type HookFunction = { __brand: 'hook-function' };
|
|
27
|
+
/**
|
|
28
|
+
* Placeholder type representing that this property is a Link to be rendered
|
|
29
|
+
* while formatting.
|
|
30
|
+
*/
|
|
31
|
+
export type LinkFunction = { __brand: 'link-function' };
|
|
32
|
+
/**
|
|
33
|
+
* Placeholder type representing that this property is a Handler function,
|
|
34
|
+
* which will be attached to some event when the message is formatted.
|
|
35
|
+
*/
|
|
36
|
+
export type HandlerFunction = { __brand: 'handler-function' };
|
|
37
|
+
|
|
38
|
+
export type IntlNumber = string | number | null | undefined;
|
|
39
|
+
export type IntlPlural = string | number | null | undefined;
|
|
40
|
+
export type IntlAny = any;
|
|
41
|
+
export type IntlDate = string | number | Date | null | undefined;
|
|
42
|
+
export type IntlTime = string | number | Date | null | undefined;
|
|
43
|
+
|
|
22
44
|
/**
|
|
23
45
|
* Messages with no rich formatting and no value interpolations are just plain
|
|
24
46
|
* strings, and are able to skip multiple formatting steps to work more
|
|
@@ -32,12 +54,16 @@ type PlainIntlMessage = string;
|
|
|
32
54
|
* the message should be typed as a `PlainIntlMessage`.
|
|
33
55
|
*/
|
|
34
56
|
type TypedIntlMessage<FormatValues extends object> = InternalIntlMessage & {
|
|
35
|
-
|
|
57
|
+
__values: FormatValues;
|
|
36
58
|
};
|
|
37
59
|
|
|
38
60
|
type AnyIntlMessage<FormatValues extends object | undefined = undefined> =
|
|
39
61
|
FormatValues extends undefined ? PlainIntlMessage : TypedIntlMessage<FormatValues>;
|
|
40
62
|
|
|
63
|
+
export interface IntlMessageGetter extends IntlMessageGetterAdditions {
|
|
64
|
+
(locale: string): InternalIntlMessage | PlainIntlMessage;
|
|
65
|
+
}
|
|
66
|
+
|
|
41
67
|
/**
|
|
42
68
|
* Getter function that retrieves a message that best matches the requested
|
|
43
69
|
* locale. `FormatValues` represents either an object containing all of the
|
|
@@ -47,7 +73,50 @@ type AnyIntlMessage<FormatValues extends object | undefined = undefined> =
|
|
|
47
73
|
*/
|
|
48
74
|
export interface TypedIntlMessageGetter<FormatValues extends object | undefined>
|
|
49
75
|
extends IntlMessageGetterAdditions {
|
|
50
|
-
|
|
76
|
+
// TODO: This is lossy and unfortunate that typing can't be propagated
|
|
77
|
+
// to the returned message type, but doing so causes problems with
|
|
78
|
+
// contravariance of the type. When returning a message getter from a
|
|
79
|
+
// function with a generic message getter return type, TypeScript will
|
|
80
|
+
// try to assert that every returned type is strictly compatible with the
|
|
81
|
+
// declared return type. In that case, if a message is missing any of the
|
|
82
|
+
// properties declared in the type, it will error. But in the other direction
|
|
83
|
+
// when passing a message getter as a parameter _into_ a function, TypeScript
|
|
84
|
+
// will do the opposite and error if the getter has any _extra_ properties
|
|
85
|
+
// from the declared type. The latter of these is definitely semantically
|
|
86
|
+
// correct, but the former is more just an annoying part of the type system
|
|
87
|
+
// because of this returned type being nested within this interface and the
|
|
88
|
+
// strict subtying that TypeScript uses.
|
|
89
|
+
(locale: string): InternalIntlMessage | PlainIntlMessage;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* A phantom type (not part of the actual typing of the object) that ensures
|
|
93
|
+
* TypeScript won't use subtyping to widen the types of multiple messages
|
|
94
|
+
* unless they are _identical_. For an example of the problem:
|
|
95
|
+
* ```typescript
|
|
96
|
+
* declare const message1: {a: string};
|
|
97
|
+
* declare const message2: {a: string, b: boolean};
|
|
98
|
+
*
|
|
99
|
+
* const foo = condition ? message1 : message2;
|
|
100
|
+
* // ^ This becomes just `{a: string}`, because it's the common base type.
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* This is fine for representing the object itself, but on messages those
|
|
104
|
+
* types represent required values at runtime, and so the greatest outer
|
|
105
|
+
* type is the only valid answer, rather than the greatest subtype.
|
|
106
|
+
*
|
|
107
|
+
* This phantom property ensures that TypeScript will never consider two
|
|
108
|
+
* messages subtypes of each other unless they contain identical properties,
|
|
109
|
+
* even the values of those properties, despite the fact that this only
|
|
110
|
+
* considers names being passed in (not sure how, but it does). The result
|
|
111
|
+
* is then always a union type of the possible types, which further
|
|
112
|
+
* downstream will ensure that all format values are always checked for all
|
|
113
|
+
* possible cases of the message.
|
|
114
|
+
*/
|
|
115
|
+
__phantom: object extends FormatValues
|
|
116
|
+
? any
|
|
117
|
+
: FormatValues extends undefined
|
|
118
|
+
? any
|
|
119
|
+
: keyof FormatValues;
|
|
51
120
|
}
|
|
52
121
|
|
|
53
122
|
/**
|
|
@@ -122,16 +191,109 @@ type FormatValuesFor<T> =
|
|
|
122
191
|
// be supplied, represented by the `never`. This prevents accidentally
|
|
123
192
|
// passing an object, even an empty one, for those strings, and allows the
|
|
124
193
|
// formatter to optimize a little more on each call.
|
|
125
|
-
[T] extends [
|
|
194
|
+
[T] extends [undefined]
|
|
126
195
|
? never
|
|
127
196
|
: // The `never` condition needs to be repeated here, but this time _without_
|
|
128
197
|
// the `[]` syntax, so that only the union elements with actual values are
|
|
129
198
|
// included in the union. Without this, the `infer U` on the latter side
|
|
130
199
|
// fails to resolve and the type just becomes `never`.
|
|
131
|
-
UnionToIntersection<
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
200
|
+
UnionToIntersection<T extends undefined ? never : T>;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* A template type for replaceable placeholder types to be defined by formatter
|
|
204
|
+
* implementations. Messages are typed using placeholder types like
|
|
205
|
+
* `HookFunction` and `HandlerFunction`, which are empty branded types
|
|
206
|
+
* indicating the _intent_ of a property, which the formatter is then able to
|
|
207
|
+
* replace with a more appropriate type. For example, a React formatter might
|
|
208
|
+
* replace a `HookFunction` with `(content: ReactNode) => ReactNode` to allow
|
|
209
|
+
* the caller to inject React elements within a string, while an AST formatter
|
|
210
|
+
* might require a specific object shape, or allow any plain object.
|
|
211
|
+
*/
|
|
212
|
+
interface FunctionTypeMap {
|
|
213
|
+
link: any;
|
|
214
|
+
hook: any;
|
|
215
|
+
handler: any;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
type ReplaceKeepingNullability<T, P> = P | Exclude<T, NonNullable<T>>;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Type implementation for applying a `FunctionTypeMap` to a given argument
|
|
222
|
+
* type `T`. For each property of `T`, if its value is one of the placeholder
|
|
223
|
+
* types, like `HookFunction`, it will be replaced by the corresponding
|
|
224
|
+
* template value from `FunctionTypes`.
|
|
225
|
+
*/
|
|
226
|
+
type MapFunctionTypes<T, FunctionTypes extends FunctionTypeMap> = {
|
|
227
|
+
[K in keyof T]: ReplaceKeepingNullability<
|
|
228
|
+
T[K],
|
|
229
|
+
NonNullable<T[K]> extends LinkFunction
|
|
230
|
+
? FunctionTypes['link']
|
|
231
|
+
: NonNullable<T[K]> extends HookFunction
|
|
232
|
+
? FunctionTypes['hook']
|
|
233
|
+
: NonNullable<T[K]> extends HandlerFunction
|
|
234
|
+
? FunctionTypes['handler']
|
|
235
|
+
: T[K]
|
|
236
|
+
>;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// Detect if T is explicitly `any`, in which case we don't want to do
|
|
240
|
+
// any replacements, since each type should be `any`.
|
|
241
|
+
// https://stackoverflow.com/a/61625831
|
|
242
|
+
type CheckStrictAny<Check, Yes, No> = (Check extends never ? true : false) extends false ? No : Yes;
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Given a `TypedIntlMessageGetter` type, return the format values type
|
|
246
|
+
* argument from it. For example:
|
|
247
|
+
*
|
|
248
|
+
* ```typescript
|
|
249
|
+
* type SomeMessage = TypedIntlMessageGetter<{name: string}>
|
|
250
|
+
* type Result = IntlMessageGetterInnerType<SomeMessage>
|
|
251
|
+
* // Result is `{name: string}`
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
type IntlMessageGetterInnerType<T extends IntlMessageGetter> =
|
|
255
|
+
T extends TypedIntlMessageGetter<infer U> ? U : never;
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Create the format values argument type for a format function using the
|
|
259
|
+
* configured `DefaultElements` for a formatter and injecting the placeholder
|
|
260
|
+
* types from `FunctionTypes`. The result is the set of required values
|
|
261
|
+
* necessary to format the message of type `T` with the calling formatter.
|
|
262
|
+
*/
|
|
263
|
+
type RequiredFormatValues<
|
|
264
|
+
Values extends IntlMessageGetter,
|
|
265
|
+
DefaultElements,
|
|
266
|
+
FunctionTypes extends FunctionTypeMap,
|
|
267
|
+
> =
|
|
268
|
+
IntlMessageGetterInnerType<Values> extends undefined
|
|
269
|
+
? never
|
|
270
|
+
: CheckStrictAny<
|
|
271
|
+
IntlMessageGetterInnerType<Values>,
|
|
272
|
+
// If `T` is explicitly `any`, then individual properties can't be typed and
|
|
273
|
+
// anything is allowed. This is only typical when consumers manually create a
|
|
274
|
+
// `TypedIntlMessageGetter` type as needed to pass values around, but loses
|
|
275
|
+
// any real type safety for the formatted values.
|
|
276
|
+
// Without this, though, `MapFunctionTypes` would always match the first
|
|
277
|
+
// value and turn every property into the mapped `LinkFunction` type, which
|
|
278
|
+
// is almost always going to be wrong.
|
|
279
|
+
any,
|
|
280
|
+
MapFunctionTypes<
|
|
281
|
+
Omit<FormatValuesFor<IntlMessageGetterInnerType<Values>>, keyof DefaultElements>,
|
|
282
|
+
FunctionTypes
|
|
283
|
+
>
|
|
137
284
|
>;
|
|
285
|
+
|
|
286
|
+
export interface IntlOptionalRichTextKeys {
|
|
287
|
+
$_: any;
|
|
288
|
+
$b: any;
|
|
289
|
+
$i: any;
|
|
290
|
+
$p: any;
|
|
291
|
+
$link: any;
|
|
292
|
+
$code: any;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export type IntlFormatFunctionValuesArg<T extends IntlMessageGetter> = RequiredFormatValues<
|
|
296
|
+
T,
|
|
297
|
+
IntlOptionalRichTextKeys,
|
|
298
|
+
FunctionTypeMap
|
|
299
|
+
>;
|