事情的起因是最近在写国际化(vue-i18n)在页面中使用 $t
渲染文本时没有出现提示,想着都用ts了,没有提示怎么看都不方便,于是就有了此篇文章。
i18n的使用这里就不多赘述了,直接看代码
1. 初始化i18n
- en.ts
// src/i18n/lang/en.ts
const en = {
title: 'Management System',
nav: {
login: 'Login',
logout: 'Logout',
},
};
export type LangTypes = typeof en;
export default en;
- zh.ts
// src/i18n/lang/zh.ts
import type { LangTypes } from './en';
const zh: LangTypes = {
title: '后台管理系统',
nav: {
login: '登录',
logout: '登出',
},
};
export default zh;
- index.ts
// src/i18n/index.ts
import { createI18n } from 'vue-i18n';
import zh from './lang/zh';
import en from './lang/en';
const { getCache } = useLocalCache();
const messages = {
en,
zh,
};
export type LangTypes = keyof typeof messages;
const i18n = createI18n({
locale: getCache('lang'),
fallbackLocale: 'zh', // 设置备用 语言
legacy: false, // 使用Composition API,必须将其设置为false
messages,
});
export default i18n;
这里我们注意到en.ts里面的类型 LangTypes,是用他来生成$t
的类型提示,不过在使用时还需要对其进行一些变换,详情见下文
2. 类型声明
- 声明之前首先要确定具体的类型,因为
$t
的第一个参数默认类型为string
,而我们在项目中使用时想要有具体的类型就需要将其定义为联合类型。
// typing/index.ts
// { a: 1, b: { ba: { baa: 1, bab: 2 }, bb: 2} } ---> a | b.ba.baa | b.ba.bab | b.bb
export type ObjKeysToUnion<T, P extends string = ''> = T extends object
? {
[K in keyof T]: ObjKeysToUnion<
T[K],
P extends '' ? `${K & string}` : `${P}.${K & string}`
>;
}[keyof T]
: P;
上面的类型ObjKeysToUnion
可以将之前en.ts
导出的LangTypes
转换为符合我们预期的联合类型,用到的ts语法不会太复杂(建议多练练type challenges
),主要是逻辑思维有一定难度。
- 有了类型声明后,可以将其挂载在
vue
的Component
上,如下所示
import { Component } from 'vue';
import type { ObjKeysToUnion } from '@/typing';
import type { LangTypes } from '@/i18n/lang/en';
declare module 'vue' {
interface ComponentCustomProperties extends Component {
$t(key: ObjKeysToUnion<LangTypes>): string;
}
}
这样定义完之后就可以在.vue文件的template中使用具有具体类型的$t
了
更多详情可以查看项目在线地址