自动摘要
正在生成中……
Q:解释一下这个 typescript脚本
export const i18n = {
defaultLocale: 'en',
locales: ['en', 'de', 'cs'],
} as const;
export type Locale = typeof i18n['locales'][number];
该 TypeScript 脚本定义了一个用于国际化(i18n)的配置,以及一个关联的类型定义。
这个脚本可以分解为两个部分:
-
i18n
对象的定义:
export const i18n = {
defaultLocale: 'en',
locales: ['en', 'de', 'cs'],
} as const;
在这里,i18n
对象包括两个属性:defaultLocale
定义了默认的区域设置,此处为 'en'
(代表英语);locales
属性是一个字符串数组,列出了支持的区域设置代码,表示应用程序支持英语、德语和捷克语。
通过使用 as const
修饰符,我们将 i18n
对象中的每一个属性都显式地标记为常量,这会使得 TypeScript 对该对象进行更为严格的类型检查,该对象的属性将不能被更改(immutable),并且 locales
数组中的值也将被视为字面量类型而非普通的字符串。
-
Locale
类型的定义:
export type Locale = typeof i18n['locales'][number];
Locale
是一个通过索引访问操作符([number]
)从 i18n['locales']
数组中提取出的类型。它相当于 i18n.locales
数组中所有可能值的联合类型,于是 Locale
类型会等同于 'en' | 'de' | 'cs'
。这意味着任何 Locale
类型的变量在赋值时只能被赋予 'en'
、'de'
或 'cs'
这三个字符串字面量中的一个。
简单来说,此脚本在声明了 app 支持的语言列表的同时,定义了一个类型,这个类型仅限于这些已声明的语言代码。
Q:export type Locale = typeof i18n['locales'][number];
这里的number
是什么?
在 TypeScript 中,当你在索引类型时使用 number
关键字,你实际上是在说:“使用任何数字来索引数组,得到数组中某个元素的类型。” 于是,typeof i18n['locales'][number]
这种用法意味着你想获取 i18n['locales']
数组中任何一个元素的类型,即,数组中每个元素的类型的联合。
在你提供的上下文中,i18n['locales']
被定义为具有具体值的字符串数组,使用 as const
进行类型施加,让 TypeScript 将其理解为一个具体值的数组,而不是任意字符串的数组。这样,当你用 number
来索引这样一个数组,你得到的是各个具体字符串值的联合类型,而不是普通的 string
类型。也就是说,对于数组 ['en', 'de', 'cs']
而言,typeof i18n['locales'][number]
的结果是类型 'en' | 'de' | 'cs'
,它表示任何赋值给该类型的变量都必须具有数组中的一个字符串值。
TypeScript 强大的功能之一就是你可以定义一个类型,这个类型严格对应于一个字面量数组中的元素。下面是使用这种方式的更多例子:
示例1:状态码
设想你的应用程序中有一组状态码,用于代表不同的状态:
const statusCodes = ['loading', 'success', 'error'] as const;
type StatusCode = typeof statusCodes[number];
然后你可以使用 StatusCode
类型确保你的函数或变量只接受这些指定的状态码:
function updateStatus(status: StatusCode) {
// 函数体在这里
}
updateStatus('loading'); // 有效
updateStatus('error'); // 有效
// updateStatus('not_found'); // TypeScript错误:“not_found”无法分配给类型'StatusCode'
示例2:配置标志
假设你有一组功能标志,用于在应用程序中切换特定功能:
const featureFlags = ['beta', 'new_ui', 'experimental_api'] as const;
type FeatureFlag = typeof featureFlags[number];
在传递功能标志时:
function enableFeature(flag: FeatureFlag) {
// 启用功能
}
enableFeature('new_ui'); // 正确的用法
// enableFeature('alpha'); // 无效用法,会导致TypeScript错误
示例3:基于角色的访问控制
考虑到一个应用程序具有基于角色的访问系统:
const userRoles = ['admin', 'editor', 'subscriber'] as const;
type UserRole = typeof userRoles[number];
然后你可以强制进行角色检查:
function performRestrictedAction(userRole: UserRole) {
if (userRole === 'admin') {
// 执行操作
}
}
performRestrictedAction('editor'); // 有效角色
// performRestrictedAction('guest'); // 类型错误...
以上例子描述了如何在 TypeScript 中创建并使用联合类型,以确保变量和函数参数与预定义的字符串字面量集合严格匹配。
Q: typescript 声明一个对象为常量,如果不使用as const关键字,其内部的属性仍然是可以改变的?是吗
是的,当你在 TypeScript 中声明一个对象时,如果不使用 as const
断言,对象内部的属性是可以修改的。as const
断言的作用是将一个对象标记为完全不可更改的常量。
在不使用 as const
的情况下,TypeScript 默认认为对象的属性是可变的。例如:
const config = {
endpoint: '<http://api.example.com>',
retries: 3,
};
// 没有使用 as const ,所以属性可以被修改
config.endpoint = '<http://api.changedexample.com>'; // 正常工作
config.retries = 5; // 正常工作
在以上代码中,config
对象的 endpoint
和 retries
属性均可被赋予新值。
然而,当使用了 as const
之后,对象及其所有嵌套的属性都会被视为字面量类型,并且不能被更改:
const config = {
endpoint: '<http://api.example.com>',
retries: 3,
} as const;
// 使用了 as const ,试图修改属性会导致错误
config.endpoint = '<http://api.changedexample.com>'; // TypeScript错误:无法分配到 'endpoint' ,因为它是只读属性。
config.retries = 5; // TypeScript错误:无法分配到 'retries' ,因为它是只读属性。
在这种情况下,as const
告诉 TypeScript 编译器对象 config
及其属性是不可更改的,任何试图修改这些属性的操作都会导致编译时错误。