From c298eae5bbd12ad9e6bb781cbf480241b4bbe9fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A9=98=E5=B9=B4?= <664037691@qq.com> Date: Thu, 12 Jan 2023 16:10:55 +0800 Subject: [PATCH] Improve: i18n type gymnastics (#116) Co-authored-by: Dreamacro <8615343+Dreamacro@users.noreply.github.com> --- src/i18n/index.ts | 14 ++++++++++++++ src/lib/type.ts | 5 +++++ src/stores/jotai.ts | 9 +++++---- 3 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 src/lib/type.ts diff --git a/src/i18n/index.ts b/src/i18n/index.ts index dd10430..798e20d 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -1,3 +1,7 @@ +import { IsEqual } from 'type-fest' + +import Infer from '@lib/type' + import en_US from './en_US' import zh_CN from './zh_CN' @@ -8,6 +12,16 @@ export const Language = { export type Lang = keyof typeof Language +type US = typeof Language.en_US +type CN = typeof Language.zh_CN + +// type guard for US and CN +type TrueGuard = T +// eslint-disable-next-line @typescript-eslint/no-unused-vars +type _equalGuard = TrueGuard, Infer>> + +export type LocalizedType = US + export const locales = Object.keys(Language) export function getDefaultLanguage (): Lang { diff --git a/src/lib/type.ts b/src/lib/type.ts new file mode 100644 index 0000000..98f6f8c --- /dev/null +++ b/src/lib/type.ts @@ -0,0 +1,5 @@ +type InferRecursion = T extends object ? { [P in keyof T]: P extends string ? InferRecursion<`${K}.${P}`, T[P]> : K }[keyof T] : K + +type Infer = Extract<{ [K in keyof T]: K extends string ? InferRecursion : unknown }[keyof T], string> + +export default Infer diff --git a/src/stores/jotai.ts b/src/stores/jotai.ts index 23ae8dc..349a3cd 100644 --- a/src/stores/jotai.ts +++ b/src/stores/jotai.ts @@ -10,13 +10,14 @@ import { useCallback, useEffect, useMemo, useRef } from 'react' import useSWR from 'swr' import { Get } from 'type-fest' -import { Language, locales, Lang, getDefaultLanguage } from '@i18n' +import { Language, locales, Lang, getDefaultLanguage, LocalizedType } from '@i18n' import { partition } from '@lib/helper' import { useWarpImmerSetter, WritableDraft } from '@lib/jotai' import { isClashX, jsBridge } from '@lib/jsBridge' import { Snapshot } from '@lib/request' import * as API from '@lib/request' import { StreamReader } from '@lib/streamer' +import Infer from '@lib/type' import * as Models from '@models' import { Log } from '@models/Log' @@ -31,9 +32,9 @@ export function useI18n () { const lang = useMemo(() => defaultLang ?? getDefaultLanguage(), [defaultLang]) const translation = useCallback( - function (namespace: Namespace) { - function t (path: Path) { - return get(Language[lang][namespace], path) as unknown as Get + function (namespace: Namespace) { + function t> (path: Path) { + return get(Language[lang][namespace], path) as unknown as Get } return { t } },