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

Cannot pass TFunction as argument #1404

Closed
Vovan-VE opened this issue Nov 12, 2021 · 2 comments
Closed

Cannot pass TFunction as argument #1404

Vovan-VE opened this issue Nov 12, 2021 · 2 comments
Assignees

Comments

@Vovan-VE
Copy link

🐛 Bug Report

An instance of TFunction<...> given from useTranslation(['a', 'b']) cannot be passed as argument to TFunction<'a'>.

To Reproduce

const TYPES = ['x', 'y'] as const;

type Type = typeof TYPES[number];

const getTitleA: Record<Type, (t: TFunction<'a'>) => string> = {
  x: (t) => t('a:x'),
  y: (t) => t('a:y'),
};

const Foo: FC = () => {
  const [tAstr] = useTranslation('a');
  const [tAarray] = useTranslation(['a']);
  const [tAB] = useTranslation(['a', 'b']);
  for (const type of TYPES) {
    // ok
    getTitleA[type](tAstr);
    
    // TS2345: Argument of type 'TFunction<"a"[], undefined>' is not assignable
    // to parameter of type 'TFunction<"a", undefined>'.
    //   Type '"a"[]' is not assignable to type '"a"'.
    getTitleA[type](tAarray);

    // TS2345: Argument of type 'TFunction<("a" | "b")[], undefined>' is
    // not assignable to parameter of type 'TFunction<"a", undefined>'.
    //   Type '("a" | "b")[]' is not assignable to type '"a"'.
    getTitleA[type](tAB);
  }

  return null;
};

So, only t from useTranslation('a') can be passed to TFunction<'a'>.

Expected behavior

An argument of type TFunction<'a'> should accept an instance of TFunction<'a' | 'b'>.

Your Environment

  • node: 16.13.0
  • typescript: 4.4.4
  • i18next: 21.4.2
  • react-i18next: 11.14.1
  • os: Linux
@Vovan-VE
Copy link
Author

In my project I temporary add extra declaration, that produce t of type TFunction<'a'> & TFunction<'b'> from useTranslation(['a', 'b']), and so it can be passed to TFunction<'a'> argument.

declare module 'react-i18next' {
  export * from 'react-i18next/';
  import { i18n } from 'i18next';
  import {
    Namespace,
    DefaultNamespace,
    KeyPrefix,
    UseTranslationOptions,
    TFunction,
  } from 'react-i18next/';

  type UnionNS<N extends Namespace> = N extends readonly (infer U)[] ? U : N;
  type UnionTFunctions<NAlt, N, TKPrefix extends KeyPrefix<N> = undefined> = NAlt extends any
    ? TFunction<NAlt, TKPrefix>
    : never;

  /**
   * Convert Union to Intersection
   *
   * ```
   * type C = UnionToIntersection<A | B>;
   * // A & B
   * ```
   *
   * @see https://github.com/microsoft/TypeScript/issues/29594
   * @see https://github.com/microsoft/TypeScript/issues/29594#issuecomment-477068575 Explanation
   */
  type UnionToIntersection<U> = (U extends any ? (u: U) => void : never) extends (
    u: infer I,
  ) => void
    ? I
    : never;

  export type TFunctionAlt<
    N extends Namespace,
    TKPrefix extends KeyPrefix<N> = undefined,
  > = UnionToIntersection<UnionTFunctions<UnionNS<N>, N, TKPrefix>>;

  type UseTranslationResponse<N extends Namespace, TKPrefix extends KeyPrefix<N>> = [
    TFunctionAlt<N, TKPrefix>,
    i18n,
    boolean,
  ] & {
    t: TFunctionAlt<N, TKPrefix>;
    i18n: i18n;
    ready: boolean;
  };

  export declare function useTranslation<
    N extends Namespace = DefaultNamespace,
    TKPrefix extends KeyPrefix<N> = undefined,
  >(
    ns?: N | Readonly<N>,
    options?: UseTranslationOptions<N, TKPrefix>,
  ): UseTranslationResponse<N, TKPrefix>;
}

@Vovan-VE
Copy link
Author

At least in my case it was fixed in 11.14.2 with #1403. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants