构造状态的联合类型
在项目中,难免会遇到一些场景需要定义状态对象,比如想表示一个页面的状态,可能会定义下面这样的状态
const PageState = {
Loading: 0,
Normal: 1,
Error: 2,
};
复制
然后我们的数据源可能会使用某个字段来引用它,比如
const pageData = {
//...
state:PageState.Loading
}
复制
但是我们希望这里的state不能是PageState键值以外的值,所以我们可以构造一个类型来限制赋值,如果PageState的键值相同就很方便,不同的话,我们就需要写一个工具类来获取状态对象所有值构成的联合类型,像这样
type IPageData = {
//.....
state:TypeRestfulMethod<typeof PageState> // 0 | 1 | 2
};
const PageState = {...} as const
type TypeRestfulMethod<T> = T[keyof T];
复制
as const 是TS的一种语法,它可以让目标对象在被解析式,不往上方扩展,比如“1”不能被解析为string,它只会被解析为类型“1”,之后我们想在状态对象上添加其他的类型,也不需要遵循相同的值类型了。
推断JSON.Prase序列化之后的类型
在请求接口时,拿回来的数据,一般都是序列化之后的字符串,我们需要先进行反序列化操作,然后再将获取到的数据进行处理,但是,我们在JSON.prase之后,得到的数据并不能推断出具体的类型
当然这个时候可以使用类型断言,让编译器知道变量的类型
但我们其实不希望每次使用JSON.parse都手动的去加一个类型断言,毕竟有的时候写类型文件和使用方法的不是同一个人,我们还是希望能写一个接口来统一推断,所以我们可以这样写
type json<T> = string & T
interface JSON {
parse<T>(text: json<T>, reviver?: (key: any, value: any) => any): T;
stringify<T>(value: T, replacer?: (key: string, value: any) => any, space?: string | number): json<T>;
stringify<T>(value: T, replacer?: (number | string)[] | null, space?: string | number): json<T>;
}
复制
之后我们就能得到准确的类型推断
type Person = {
name: string;
age: number;
};
const role: Person = {
name: 'jj',
age: 20,
};
const str: json<Person> = JSON.stringify(role);
const x = JSON.parse(str);
复制
为配置文件定义类型
一般情况下,我们的项目都会有多个环境的配置文件,比如.dev,.pro,.release这些,但是里面配置对象的键值都是一样的,像这样。
proConfig = {
//....
url:...
api:...
corpid:...
}
devConfig = {
//....
url:...
api:...
corpid:...
}
releaseConfig = {
//....
url:...
api:...
corpid:...
}
复制
但是有时候,我们根据需要去测试环的境配置文件添加一个变量,可能就忘记了去正式和发布环境添加,导致之后打包上线出问题,这个时候我们应该为他们定义一个相同类型,来避免这种情况。
我们可以直接定义一个对应的type,然后赋给三个对象
type IConfig = {
//....
url:string
api:string
corpid:string
}
复制
但是这样有一个问题,就是后续我们如果加新的变量,就需要一直修改类型,显然这很麻烦,所以我们可以根据一个配置文件,来逆向的推导出类型,以dev为例子
const devConfig = {
url: 'xxx',
cropid: '123',
};
type IConfigUtil<T extends object> = {
[P in keyof T]: T[P];
};
type IConfig = IConfigUtil<typeof devConfig>;
复制
写在最后
感谢你能看到这里,本文记录了几个关于TS的优化,其实相关的还有很多,后续会陆陆续续的更新,希望对你有所帮助,如果你发现了问题和更好的解决方案,欢迎留言一起讨论