Skip to content

Commit

Permalink
Code cleanup of TFunction tests, allow strong vs open generics on int…
Browse files Browse the repository at this point in the history
…erpolation values (#1205)

* cleanup/combine tests

* rename

* cleanup

* TOptions should use an indexer (StringMap) by default, but allow strong typing of interpolation values as well

* comments
  • Loading branch information
rosskevin committed Feb 13, 2019
1 parent 55bcc6d commit 4e4c75b
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 144 deletions.
23 changes: 15 additions & 8 deletions index.d.ts
Expand Up @@ -395,10 +395,6 @@ declare namespace i18next {
compatibilityJSON?: 'v1' | 'v2' | 'v3';
}

// Add an indexer to assure that interpolation arguments can be passed
type TOptions<TCustomOptions extends object = object> = TOptionsBase &
TCustomOptions & { [key: string]: any };

interface TOptionsBase {
/**
* Default value to return if a translation was not found
Expand Down Expand Up @@ -458,13 +454,24 @@ declare namespace i18next {
interpolation?: InterpolationOptions;
}

// indexer that is open to any value
type StringMap = { [key: string]: any };

/**
* Options that allow open ended values for interpolation unless type is provided.
*/
type TOptions<TInterpolationMap extends object = StringMap> = TOptionsBase & TInterpolationMap;

type Callback = (error: any, t: TFunction) => void;

/**
* Uses similar args as the t function and returns true if a key exists.
*/
interface ExistsFunction<TKeys extends string = string, TValues extends object = object> {
(key: TKeys | TKeys[], options?: TOptions<TValues>): boolean;
interface ExistsFunction<
TKeys extends string = string,
TInterpolationMap extends object = StringMap
> {
(key: TKeys | TKeys[], options?: TOptions<TInterpolationMap>): boolean;
}

interface WithT {
Expand All @@ -475,10 +482,10 @@ declare namespace i18next {
type TFunction = <
TResult extends string | object | Array<string | object> | undefined = string,
TKeys extends string | TemplateStringsArray = string,
TValues extends object = object
TInterpolationMap extends object = StringMap
>(
key: TKeys | TKeys[],
options?: TOptions<TValues>,
options?: TOptions<TInterpolationMap>,
) => TResult;

interface Resource {
Expand Down
113 changes: 113 additions & 0 deletions test/typescript/t.test.ts
@@ -0,0 +1,113 @@
import i18next from 'i18next';

function basicUsage(t: i18next.TFunction) {
t('friend');
t`friend`;
t(['friend', 'tree']);
t('friend', { myVar: 'someValue' });
t(['friend', 'tree'], { myVar: 'someValue' });
}

/**
* Use of the exported TFunction in external utility methods such as
* NamespaceConsumer children t
*/
function returnCasts(t: i18next.TFunction) {
const s: string = t('friend'); // same as <string>
const s2: string = t`friend`;
const o: object = t<object>('friend');
const sa: string[] = t<string[]>('friend');
const oa: object[] = t<object[]>('friend');
}

function callsMethodWithOptionalArg(t: i18next.TFunction) {
function displayHint(hint?: string) {
return String(hint);
}
displayHint(t('friend'));
}

function callsMethodWithRequiredArg(t: i18next.TFunction) {
function displayHint(hint: string) {
return String(hint);
}
displayHint(t('friend'));
}

function arrayKey(t: i18next.TFunction) {
const error404 = '404';
t([`error.${error404}`, 'error.unspecific']); // -> "The page was not found"

const error502 = '502';
t([`error.${error502}`, 'error.unspecific']); // -> "Something went wrong"
}

function stringKey(t: i18next.TFunction) {
t('No one says a key can not be the fallback.');
// -> "Niemand sagt ein key kann nicht als Ersatz dienen."

t('This will be shown if the current loaded translations to not have this.');
// -> "This will be shown if the current loaded translations to not have this."
}

function interpolation(t: i18next.TFunction) {
// key = 'hello {{what}}'
t('key', { what: i18next.format('world', 'uppercase') }); // -> hello WORLD

t('key', { what: 'i18next', how: 'great' });

const author = {
name: 'Jan',
github: 'jamuhl',
};
t('key', { author });

t('keyEscaped', { myVar: '<img />' });
// -> "no danger &lt;img &#x2F;&gt;"

t('keyUnescaped', { myVar: '<img />' });
// -> "dangerous <img />"

t('keyEscaped', {
myVar: '<img />',
interpolation: { escapeValue: false },
});
// -> "no danger <img />" (obviously could be dangerous)

t('key', { count: 0 }); // -> "items"
t('key', { count: 1 }); // -> "item"
t('key', { count: 5 }); // -> "items"
t('key', { count: 100 }); // -> "items"
t('keyWithCount', { count: 0 }); // -> "0 items"
t('keyWithCount', { count: 1 }); // -> "1 item"
t('keyWithCount', { count: 5 }); // -> "5 items"
t('keyWithCount', { count: 100 }); // -> "100 items"

t('key1_interval', { postProcess: 'interval', count: 1 }); // -> "one item"
t('key1_interval', { postProcess: 'interval', count: 4 }); // -> "a few items"
t('key1_interval', { postProcess: 'interval', count: 100 }); // -> "a lot of items"

// not matching into a range it will fallback to
// the regular plural form
t('key2_interval', { postProcess: 'interval', count: 1 }); // -> "one item"
t('key2_interval', { postProcess: 'interval', count: 4 }); // -> "a few items"
t('key2_interval', { postProcess: 'interval', count: 100 }); // -> "100 items"
t('friend', { context: 'male', count: 1 }); // -> "A boyfriend"
t('friend', { context: 'female', count: 100 }); // -> "100 girlfriends"
t('tree', { returnObjects: true, something: 'gold' });
// -> { res: 'added gold' }

t('array', { returnObjects: true });
// -> ['a', 'b', 'c']
t('arrayJoin', { joinArrays: '+' });
// -> "line1+line2+line3"

t('arrayJoinWithInterpolation', {
myVar: 'interpolate',
joinArrays: ' ',
});
// -> "you can interpolate"

t('arrayOfObjects.0.name');
// -> "tom"
}
37 changes: 0 additions & 37 deletions test/typescript/t.tfunction.test.ts

This file was deleted.

86 changes: 0 additions & 86 deletions test/typescript/t.witht.test.ts

This file was deleted.

29 changes: 16 additions & 13 deletions test/typescript/tGeneric.test.ts
@@ -1,27 +1,30 @@
import i18next from 'i18next';

interface CustomOptions {
interface InterpolationValues {
myVar: string;
}
type KeyList = 'friend' | 'tree';
type Keys = 'friend' | 'tree';

i18next.t<string, KeyList>('friend', { myVar: 'someValue' });
i18next.t<string, KeyList>(['friend', 'tree'], { myVar: 'someValue' });
i18next.t<string, KeyList, { myVar: 'someValue' }>('friend', { myVar: 'someValue' });
i18next.t<string, KeyList, { myVar: 'someValue' }>(['friend', 'tree'], { myVar: 'someValue' });
// check keys
i18next.t<string, Keys>('friend', { myVar: 'someValue' });
i18next.t<string, Keys>(['friend', 'tree'], { myVar: 'someValue' });

// check interpolation values
i18next.t<string, Keys, InterpolationValues>('friend', { myVar: 'someValue' });
i18next.t<string, Keys, InterpolationValues>(['friend', 'tree'], { myVar: 'someValue' });

// NOTION: disable no-unnecessary-generics for generic pattern test.
/* tslint:disable:no-unnecessary-generics */
interface ExWithT extends i18next.WithT {
t<Keys extends KeyList = KeyList, Val extends object = object, R = string>(
keys: Keys | Keys[],
t<CustomKeys extends Keys = Keys, Val extends object = object, R = string>(
keys: CustomKeys | CustomKeys[],
options?: i18next.TOptions<Val>,
): R;
t<Keys extends OtherKeyList = OtherKeyList, Val extends object = object, R = string>(
keys: Keys | Keys[],
t<CustomKeys extends OtherKeyList = OtherKeyList, Val extends object = object, R = string>(
keys: CustomKeys | CustomKeys[],
options?: i18next.TOptions<Val>,
): R;
t<Keys extends string = KeyList, R = string>(keys: Keys | Keys[]): R;
t<CustomKeys extends string = Keys, R = string>(keys: CustomKeys | CustomKeys[]): R;
}

type OtherKeyList = 'private' | 'public';
Expand All @@ -32,9 +35,9 @@ type OtherKeyList = 'private' | 'public';
(i18next as ExWithT).t('public');
(i18next as ExWithT).t('friend', {});
(i18next as ExWithT).t('private', {});
(i18next as ExWithT).t<KeyList, { myVar: 'someValue' }>('friend', { myVar: 'someValue' });
(i18next as ExWithT).t<Keys, { myVar: 'someValue' }>('friend', { myVar: 'someValue' });
(i18next as ExWithT).t<OtherKeyList, { myVar: 'someValue' }>('private', { myVar: 'someValue' });
const result = (i18next as ExWithT).t<KeyList, { myVar: 'someValue' }, { result: 'result' }>(
const result = (i18next as ExWithT).t<Keys, { myVar: 'someValue' }, { result: 'result' }>(
'friend',
{ myVar: 'someValue' },
);
Expand Down

0 comments on commit 4e4c75b

Please sign in to comment.