让我们说我有一个代表用户登录并给出姓名的类型:
export interface User {
login: string;
name: string;
}
现在我想使用该用户的登录信息查询API:
const formatLogin (login: string) => login.toLowerCase().trim();
const getUser = (login: string) => ajax(`${API}/users/${formatLogin(login)}`);
这很有效。稍后在我的代码中我可能会使用:
getUser(this.user.name);
但这是一个错误。它应该是this.user.login
。它们都是字符串,因此TypeScript不会抱怨。
有没有办法强制使用TypeScript来使用属性?
我已尝试使用export type Username = string
,但这不起作用,因为name
仍然满足该类型,因为它只是string
。我还尝试了export interface Username {}
formatLogin
,它不适用toLowerCase
,这需要字符串方法trim
和interface Username extends String {}
。 {{1}}有同样的问题。
答案 0 :(得分:4)
一种方法是使用所谓的branded types,遵循TypeScript compiler itself来源中设置的示例:
export type LoginString = string & { __loginBrand: any};
export interface User {
login: LoginString;
name: string;
}
const formatLogin = (login: string) => login.toLowerCase().trim();
const getUser = (login: LoginString) => { };
let user: User;
formatLogin(user.login); // ok
getUser(user.name); // error
在初始化LoginString
对象时,您必须手动将普通字符串转换为User
类型,您不必为__loginBrand
提供实际值 - 它'仅用于类型检查。
@estus建议的另一种方法是使用destructured对象作为API的一种命名参数。它没有经过类型检查,但任何不匹配都非常明显,只要所有名称始终在所有地方使用:
export interface GetUser {
login: string;
}
const getUser = ({login}: GetUser) => {}
getUser(user); // ok
getUser({login: user.name}); // huh?
答案 1 :(得分:1)
键入系统不可能区分这两个属性,因为它们是任意字符串。
这是测试覆盖率有助于消除人为错误的领域。
问题不是用类型解决,而是用合理的方法命名方法解决,不会留下任何错误。例如,这个错误
(x1,x[n+1])
更有可能吸引注意力。
根据具体情况,getUserByLogin(this.user.name);
不能接受字符串,而是接受对整个对象的引用,并从中获取所需的属性:
getUser