Skip to content

Commit

Permalink
Merge 921ae23 into 270904f
Browse files Browse the repository at this point in the history
  • Loading branch information
rosskevin committed Jan 23, 2019
2 parents 270904f + 921ae23 commit 16a1d3d
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 84 deletions.
54 changes: 23 additions & 31 deletions index.d.ts
Expand Up @@ -313,7 +313,7 @@ declare namespace i18next {
* Sets defaultValue
* @default args => ({ defaultValue: args[1] })
*/
overloadTranslationOptionHandler?(args: string[]): TranslationOptions;
overloadTranslationOptionHandler?(args: string[]): TOptions;

/**
* @see https://www.i18next.com/interpolation.html
Expand Down Expand Up @@ -396,10 +396,10 @@ declare namespace i18next {
}

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

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

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

interface TranslationFunction<
TResult = any,
TValues extends object = object,
TKeys extends string = string
> {
(key: TKeys | TKeys[], options?: TranslationOptions<TValues>): TResult;
}
type Callback = (error: any, t: TFunction) => void;

/**
* WARNING: Order with generic type parameters are different from TranslationFunction for usability
* see: https://github.com/i18next/react-i18next/issues/662
* 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 WithT {
/**
* Please have a look at the translation functions like interpolation, formatting and plurals for more details on using it.
*/
// Expose parameterized t in the i18next interface hierarchy
t<
TResult extends string | object | Array<string | object> = string,
TKeys extends string = string,
TValues extends object = object,
TResult extends string | object | Array<string | object> =
| string
| object
| Array<string | object>
TValues extends object = object
>(
key: TKeys | TKeys[],
options?: TranslationOptions<TValues>,
options?: TOptions<TValues>,
): TResult;
}

type TFunction = WithT['t'];

interface Resource {
[language: string]: ResourceLanguage;
}
Expand Down Expand Up @@ -573,7 +565,7 @@ declare namespace i18next {
/** Unique name */
name: string;
type: 'postProcessor';
process(value: string, key: string, options: TranslationOptions, translator: any): string;
process(value: string, key: string, options: TOptions, translator: any): string;
}

/**
Expand Down Expand Up @@ -611,8 +603,8 @@ declare namespace i18next {
* @param options - Initial options.
* @param callback - will be called after all translations were loaded or with an error when failed (in case of using a backend).
*/
init(callback?: Callback): Promise<TranslationFunction>;
init(options: InitOptions, callback?: Callback): Promise<TranslationFunction>;
init(callback?: Callback): Promise<TFunction>;
init(options: InitOptions, callback?: Callback): Promise<TFunction>;

loadResources(callback?: (err: any) => void): void;

Expand All @@ -633,23 +625,23 @@ declare namespace i18next {
services: Services;

/**
* Uses the same resolve functionality as the t function and returns true if a key exists.
* Uses similar args as the t function and returns true if a key exists.
*/
exists: TranslationFunction<boolean>;
exists: ExistsFunction;

/**
* Returns a t function that defaults to given language or namespace.
* Both params could be arrays of languages or namespaces and will be treated as fallbacks in that case.
* On the returned function you can like in the t function override the languages or namespaces by passing them in options or by prepending namespace.
*/
getFixedT(lng: string | string[], ns?: string | string[]): TranslationFunction;
getFixedT(lng: null, ns: string | string[]): TranslationFunction;
getFixedT(lng: string | string[], ns?: string | string[]): TFunction;
getFixedT(lng: null, ns: string | string[]): TFunction;

/**
* Changes the language. The callback will be called as soon translations were loaded or an error occurs while loading.
* HINT: For easy testing - setting lng to 'cimode' will set t function to always return the key.
*/
changeLanguage(lng: string, callback?: Callback): Promise<TranslationFunction>;
changeLanguage(lng: string, callback?: Callback): Promise<TFunction>;

/**
* Is set to the current detected or set language.
Expand Down
8 changes: 8 additions & 0 deletions test/typescript/exists.test.ts
@@ -0,0 +1,8 @@
import i18next from 'i18next';

i18next.exists('friend');
i18next.exists(['friend', 'tree']);
i18next.exists('friend', { myVar: 'someValue' });
i18next.exists(['friend', 'tree'], { myVar: 'someValue' });

const a: boolean = i18next.exists('my.key');
52 changes: 22 additions & 30 deletions test/typescript/init.test.ts
Expand Up @@ -13,9 +13,9 @@ i18next.init(
},
},
},
(err: any, t: i18next.TranslationFunction) => {
(err, t) => {
// initialized and ready to go!
const value: string = i18next.t('key');
const value: string = t('key');
},
);

Expand All @@ -38,7 +38,8 @@ i18next.init(
},
},
},
(err: any, t: i18next.TranslationFunction) => {
// not necessary but check a non-inferred arg for paranoia's sake
(err, t: i18next.TFunction) => {
// init set content
updateContent();
},
Expand All @@ -49,13 +50,13 @@ i18next.init(
ns: ['common', 'moduleA', 'moduleB'],
defaultNS: 'moduleA',
},
(err: any, t: i18next.TranslationFunction) => {
i18next.t('myKey'); // key in moduleA namespace (defined default)
i18next.t('common:myKey'); // key in common namespace
(err, t) => {
t('myKey'); // key in moduleA namespace (defined default)
t('common:myKey'); // key in common namespace
},
);

i18next.loadNamespaces('anotherNamespace', (err: any, t: i18next.TranslationFunction) => {
i18next.loadNamespaces('anotherNamespace', (err, t) => {
/* ... */
});

Expand Down Expand Up @@ -154,7 +155,7 @@ i18next.init(
crossDomain: true,
},
},
(err: any, t: i18next.TranslationFunction) => {
(err, t) => {
// init set content
updateContent2();
},
Expand All @@ -179,7 +180,7 @@ i18next.init(
defaultNS: 'file1',
debug: true,
},
(err: any, t: i18next.TranslationFunction) => {
(err, t) => {
if (err) {
console.log('something went wrong loading', err);
return;
Expand All @@ -189,7 +190,7 @@ i18next.init(
);

// with only callback
i18next.init((err: any, t: i18next.TranslationFunction) => {
i18next.init((err, t) => {
if (err) {
console.log('something went wrong loading', err);
return;
Expand All @@ -198,7 +199,6 @@ i18next.init((err: any, t: i18next.TranslationFunction) => {
});

const v: string = i18next.t('my.key');
const a: boolean = i18next.exists('my.key');

// fix language to german
const de = i18next.getFixedT('de');
Expand All @@ -208,28 +208,25 @@ const z: string = de('myKey');
const anotherNamespace = i18next.getFixedT(null, 'anotherNamespace');
const x: string = anotherNamespace('anotherNamespaceKey'); // no need to prefix ns i18n.t('anotherNamespace:anotherNamespaceKey');

i18next.changeLanguage('en', (err: any, t: i18next.TranslationFunction) => {
i18next.changeLanguage('en', (err, t) => {
if (err) {
console.log('something went wrong loading', err);
return;
}
t('key'); // -> same as i18next.t
});

i18next.loadNamespaces('myNamespace', (err: any, t: i18next.TranslationFunction) => {
i18next.loadNamespaces('myNamespace', (err, t) => {
/* resources have been loaded */
});
i18next.loadNamespaces(['myNamespace1', 'myNamespace2'], (err, t) => {
/* resources have been loaded */
});
i18next.loadNamespaces(
['myNamespace1', 'myNamespace2'],
(err: any, t: i18next.TranslationFunction) => {
/* resources have been loaded */
},
);

i18next.loadLanguages('de', (err: any, t: i18next.TranslationFunction) => {
i18next.loadLanguages('de', (err, t) => {
/* resources have been loaded */
});
i18next.loadLanguages(['de', 'fr'], (err: any, t: i18next.TranslationFunction) => {
i18next.loadLanguages(['de', 'fr'], (err, t) => {
/* resources have been loaded */
});

Expand Down Expand Up @@ -259,7 +256,7 @@ const newInstance = i18next.createInstance(
defaultNS: 'file1',
debug: true,
},
(err: any, t: i18next.TranslationFunction) => {
(err, t) => {
if (err) {
console.log('something went wrong loading', err);
return;
Expand All @@ -276,7 +273,7 @@ newInstance.init(
defaultNS: 'file1',
debug: true,
},
(err: any, t: i18next.TranslationFunction) => {
(err, t) => {
if (err) {
console.log('something went wrong loading', err);
return;
Expand All @@ -292,7 +289,7 @@ const newInstance2 = i18next.cloneInstance(
defaultNS: 'file1',
debug: true,
},
(err: any, t: i18next.TranslationFunction) => {
(err, t) => {
if (err) {
console.log('something went wrong loading', err);
return;
Expand All @@ -310,7 +307,7 @@ newInstance.init(
defaultNS: 'file1',
debug: true,
},
(err: any, t: i18next.TranslationFunction) => {
(err, t) => {
if (err) {
console.log('something went wrong loading', err);
return;
Expand Down Expand Up @@ -422,8 +419,3 @@ i18next.init({
},
},
});

i18next.exists('friend');
i18next.exists(['friend', 'tree']);
i18next.exists('friend', { myVar: 'someValue' });
i18next.exists(['friend', 'tree'], { myVar: 'someValue' });
8 changes: 8 additions & 0 deletions test/typescript/mock.test.ts
@@ -0,0 +1,8 @@
import i18next from 'i18next';

/**
* Exercises in mocking
*/
const mockWithT: i18next.WithT = {
t: ((key: string) => key) as any,
};
6 changes: 6 additions & 0 deletions test/typescript/t.test.ts
Expand Up @@ -76,3 +76,9 @@ i18next.t('friend');
i18next.t(['friend', 'tree']);
i18next.t('friend', { myVar: 'someValue' });
i18next.t(['friend', 'tree'], { myVar: 'someValue' });

// various returns <string> is the default
const s: string = i18next.t('friend'); // same as <string>
const o: object = i18next.t<object>('friend');
const sa: string[] = i18next.t<string[]>('friend');
const oa: object[] = i18next.t<object[]>('friend');
29 changes: 6 additions & 23 deletions test/typescript/tGeneric.test.ts
Expand Up @@ -5,38 +5,21 @@ interface CustomOptions {
}
type KeyList = 'friend' | 'tree';

const t1: i18next.TranslationFunction = (
key: string | string[],
options?: i18next.TranslationOptions,
) => '';
const t2: i18next.TranslationFunction<{ value: string }> = (
key: string | string[],
options?: i18next.TranslationOptions,
) => ({ value: 'asd' });
const t3: i18next.TranslationFunction<string, CustomOptions> = (
key: string | string[],
options?: i18next.TranslationOptions<CustomOptions>,
) => '';
const t4: i18next.TranslationFunction<string, object, KeyList> = (
key: KeyList | KeyList[],
options?: i18next.TranslationOptions,
) => '';

i18next.t<KeyList>('friend', { myVar: 'someValue' });
i18next.t<KeyList>(['friend', 'tree'], { myVar: 'someValue' });
i18next.t<KeyList, { myVar: 'someValue' }>('friend', { myVar: 'someValue' });
i18next.t<KeyList, { myVar: 'someValue' }>(['friend', 'tree'], { myVar: 'someValue' });
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' });

// 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[],
options?: i18next.TranslationOptions<Val>,
options?: i18next.TOptions<Val>,
): R;
t<Keys extends OtherKeyList = OtherKeyList, Val extends object = object, R = string>(
keys: Keys | Keys[],
options?: i18next.TranslationOptions<Val>,
options?: i18next.TOptions<Val>,
): R;
t<Keys extends string = KeyList, R = string>(keys: Keys | Keys[]): R;
}
Expand Down

0 comments on commit 16a1d3d

Please sign in to comment.