你怎么能只允许有效路径到模板文字类型中的嵌套对象?
How can you only allow valid paths to a nested object in a template literal type?
我基本上有2个对象。根据所选键,我希望字符串的第二部分成为对象中所选键值的键。在示例中,在 t("common.")
中的 .
之后,只应允许 "test" | "test2"
。
const common = {
"test": "Test",
"test2": "Test2"
}
const greetings = {
"hello": "Hello"
}
export type I18nMap = Record<typeof locales[number], I18nNamespace>;
export interface I18nNamespace {
common: typeof common;
greetings: typeof greetings;
}
export const locales = (["en", "nl"] as const);
type Interpolation = Record<string, string | number>
export function useTranslation<
T extends keyof I18nNamespace,
U extends T extends T ? keyof I18nNamespace[T] : never,
V extends `${T}.${U}`
>(namespace: T | T[]): {t: (key: V, interpolation?: Interpolation) => void} {
// ...
}
const { t } = useTranslation(["common", "greetings"])
// Only allow common.test | common.test2
t("common.")
尝试使用这种类型:
type GetI18nKeys<T extends keyof I18nNamespace> = T extends unknown
? `${T}.${Extract<keyof I18nNamespace[T], string>}`
: never;
这分发了T
,并在T
和keyof I18nNamespace[T]
的基础上做了一个字符串类型。如果没有 Extract<..., string>
,TypeScript 会抱怨,因为 属性 键可以是 symbol
,它不能分配给模板文字中的类型(string | number | bigint | boolean
)。
在这种情况下,您也不需要 U
和 V
类型参数。
export function useTranslation<T extends keyof I18nNamespace>(
namespace: T | T[]
): {t: (key: GetI18nKeys<T>, interpolation?: Interpolation) => void} {
// ...
}
const { t } = useTranslation(["common", "greetings"])
// Works
t('common.test')
t('common.test2')
t('greetings.hello')
// Fails
t('common.hello')
t('greetngs.test')
我基本上有2个对象。根据所选键,我希望字符串的第二部分成为对象中所选键值的键。在示例中,在 t("common.")
中的 .
之后,只应允许 "test" | "test2"
。
const common = {
"test": "Test",
"test2": "Test2"
}
const greetings = {
"hello": "Hello"
}
export type I18nMap = Record<typeof locales[number], I18nNamespace>;
export interface I18nNamespace {
common: typeof common;
greetings: typeof greetings;
}
export const locales = (["en", "nl"] as const);
type Interpolation = Record<string, string | number>
export function useTranslation<
T extends keyof I18nNamespace,
U extends T extends T ? keyof I18nNamespace[T] : never,
V extends `${T}.${U}`
>(namespace: T | T[]): {t: (key: V, interpolation?: Interpolation) => void} {
// ...
}
const { t } = useTranslation(["common", "greetings"])
// Only allow common.test | common.test2
t("common.")
尝试使用这种类型:
type GetI18nKeys<T extends keyof I18nNamespace> = T extends unknown
? `${T}.${Extract<keyof I18nNamespace[T], string>}`
: never;
这分发了T
,并在T
和keyof I18nNamespace[T]
的基础上做了一个字符串类型。如果没有 Extract<..., string>
,TypeScript 会抱怨,因为 属性 键可以是 symbol
,它不能分配给模板文字中的类型(string | number | bigint | boolean
)。
在这种情况下,您也不需要 U
和 V
类型参数。
export function useTranslation<T extends keyof I18nNamespace>(
namespace: T | T[]
): {t: (key: GetI18nKeys<T>, interpolation?: Interpolation) => void} {
// ...
}
const { t } = useTranslation(["common", "greetings"])
// Works
t('common.test')
t('common.test2')
t('greetings.hello')
// Fails
t('common.hello')
t('greetngs.test')