Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add generic type to t function and the tests #665

Merged
merged 13 commits into from
Jan 18, 2019
13 changes: 8 additions & 5 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ interface ReactI18nextModule {
init: (instance: i18next.i18n) => void;
}

interface ReactI18nextTranslateFunction {
t<TKeys extends string=string,TVals extends object =object,TResult=any>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have something more specific than any for TResult ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was migrated to i18next https://github.com/i18next/i18next/blob/master/index.d.ts#L463-L480 .
Default value is set any before I imprement tha,but I understood TrResult may be more strict as you mean.

Copy link
Contributor

@VincentLanglet VincentLanglet Jan 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree default value was set any before your implementation. But since you're working on the generic, it was perfect time to follow the boyscout rule. There is so much any in these typings.

Since this library is restricted for react use only, it's easier to have more strict type. Something like string

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@VincentLanglet I take a different tact. Incremental improvement. any is something and if you feel strongly about improving the type then PR it. Something is better than nothing, in this case any is better than no type at all so I don't pile on and ask for more changes that are near but not necessarily the intent. Otherwise it would spider into fixing all any types. I encourage small focused PRs if possible. Review/test/merge them quickly and let everyone contribute to their concern.

Copy link
Contributor

@VincentLanglet VincentLanglet Jan 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not talking about fixing all any types. Just the one he's introducing.
You ask for my contribution, but this code isn't already in the base code.
When we write code, we write test too, we don't explain "Someone can do the test for me". It's the same here.

I don't file it's difficult to change TResult = any by something like TResult extends string = string.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rosskevin I have no worry to make a PR, and I did : i18next/i18next#1175
I waiting review to check what is the best type to use.

But it's not a good idea to accept every changes of code with a fake rule of "Incremental improvement". On the contrary, it's the best way to add bugs or worse, technical debt.
In the previous PR, the type introduced could be more specific and even has typos in the commentary. The best is the enemy of good, but always ask yourself if what you're doing is really good.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @VincentLanglet - you just proved my point and incrementally improved the project!

(key: TKeys | TKeys[], options?: i18next.TranslationOptions<TVals>):TResult;
}

export const reactI18nextModule: ReactI18nextModule;

export function setDefaults(options: ReactI18NextOptions): void;
Expand All @@ -24,9 +29,8 @@ export function setI18n(instance: i18next.i18n): void;

export function getI18n(): i18next.i18n;

export interface I18nContextValues {
export interface I18nContextValues extends ReactI18nextTranslateFunction{
i18n: i18next.i18n;
t: i18next.TranslationFunction;
defaultNS?: string;
reportNS?: string;
lng?: string;
Expand Down Expand Up @@ -76,7 +80,7 @@ export interface NamespacesConsumerProps extends ReactI18NextOptions {
initialI18nStore?: {};
initialLanguage?: string;
children: (
t: i18next.TranslationFunction,
t: ReactI18nextTranslateFunction['t'],
options: {
i18n: i18next.i18n;
lng: string;
Expand All @@ -96,12 +100,11 @@ export interface I18nextProviderProps {

export const I18nextProvider: React.ComponentClass<I18nextProviderProps>;

export interface TransProps {
export interface TransProps extends Partial<ReactI18nextTranslateFunction>{
i18nKey?: string;
count?: number;
parent?: React.ReactNode;
i18n?: i18next.i18n;
t?: i18next.TranslationFunction;
defaults?: string;
values?: {};
components?: React.ReactNode[];
Expand Down
78 changes: 78 additions & 0 deletions test/typescript/GenericTlanslateFunction.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import * as React from "react";
import {
NamespacesConsumer,
Trans,
withNamespaces,
WithNamespaces,
} from "../../src/index";

type TKeys = "title" | "text";

declare interface IWithNamespacesOverrideTest extends WithNamespaces {
t<T extends string| string[]>(a: T): any;
}

function NamespacesConsumerTest() {
return (
<NamespacesConsumer>
{(t, { i18n }) =>
<div>
<h2>{t<TKeys>("title")}</h2>
<span>{t("any")}</span>
<span>{t("any", {anyObject: {}})}</span>
<span>{t<TKeys>("text")}</span>
<span>{t<TKeys, {key: string}>("text", {key: "foo"})}</span>
</div>
}
</NamespacesConsumer>
);
}

function TransComponentTest({ t }: WithNamespaces) {
return (
<div>
<Trans i18nKey="description.part1">
To get started, edit <code>src/App.js</code> and save to reload.
</Trans>
<Trans i18nKey="description.part1">
To get started, <strong title={t<TKeys>("title")}>{{name}}</strong>and save to reload.
</Trans>
</div>
);
}

const MyComponentWrapped = withNamespaces()(TransComponentTest);

type ArticleKeys = "article.part1" | "article.part2";
type AnotherArticleKeys = "anotherArticle.part1" | "anotherArticle.part2";
class App extends React.Component<WithNamespaces> {
public render() {
const { t, i18n } = this.props;

const changeLanguage = (lng: string) => {
i18n.changeLanguage(lng);
};

return (
<div className="App">
<div className="App-header">
<NamespacesConsumerTest />
<button onClick={() => changeLanguage("de")}>de</button>
<button onClick={() => changeLanguage("en")}>en</button>
</div>
<div className="App-intro">
<MyComponentWrapped />
</div>
<article>
<div>{t<ArticleKeys>("article.part1")}</div>
<div>{t<ArticleKeys>("article.part2")}</div>
</article>
<article>
<div>{t<AnotherArticleKeys>("anotherArticle.part1")}</div>
<div>{t<AnotherArticleKeys>("anotherArticle.part2")}</div>
</article>
</div>
);
}
}
export default withNamespaces("translation")(App);