打字稿:在没有实际扩展/扩大的情况下捕获/推断类型的约束
Posted
技术标签:
【中文标题】打字稿:在没有实际扩展/扩大的情况下捕获/推断类型的约束【英文标题】:Typescript: capture/infer type with constraint without actually extending/widening 【发布时间】:2022-01-23 06:28:16 【问题描述】:我有一个函数build
,它使用传入的参数构建一个User
。我想键入函数,以便它知道传入了哪些参数并在返回值中考虑它们。
我的第一个想法是捕获params
的类型并在返回类型中使用它:
type User = name: string; admin: boolean ;
const build = <T extends User>(params: T): T => params
效果很好,我现在可以这样做:
build( name: 'Jan', admin: true );
// return type is: name: string, admin: true as desired (notice admin: true, not boolean)
但是,使用<T extends User>
约束来捕获类型似乎会产生意想不到的结果,即也会扩大类型。现在允许以下内容没有错误:
build( name: 'Jan', admin: true, foo: 'shouldnt be allowed' );
// return type is: name: string, admin: true, foo: string
如果我采用一种更简单的方法,即不捕获类型而只返回User
,我会得到所需/预期的行为,其中传递User
上不存在的参数会导致错误,但该函数返回一个User
,而没有来自参数的额外类型信息:
const build = (params: User): User => params
// Type error as desired: Object literal may only specify known
// properties, and 'foo' does not exist in type 'User'
build( name: 'Jan', admin: true, foo: 'sdf' )
我想知道是否有一种方法可以捕获params
的类型,以便我可以在返回类型中使用它,但不会扩大类型。
【问题讨论】:
您的要求不明确。你希望它接受User
的子类型吗?如果是,第一种方法是正确的。您需要在调用它时明确定义泛型。如果不是,则第二种方法是正确的,
术语和语义说明:额外的属性不会加宽类型,它们会缩小类型。像inferface A a: string
这样的类型意味着“任何对象,只要它在键a
处具有string
-valued 属性”。这就是结构类型和接口可扩展性的工作原理:对于interface B extends A b: number
,每个B
也是一个A
。而且B
比A
窄,因为不是每个A
也是B
。
【参考方案1】:
Typescript 没有实现exact types,但您可以对参数类型创建自定义限制。例如(playground):
type User = name: string; admin: boolean ;
const build = <T extends User>(
params: keyof T extends keyof User ? T : never
): T => params
// const user1: name: string; admin: true;
const user1 = build( name: 'Jan', admin: true, );
// ERROR
const user2 = build( name: 'Jan', admin: true, x: 1);
同样的想法,更冗长,但更好的错误处理:
const build = <T extends User>(
params: keyof T extends keyof User ? T : User
): Pick<T, keyof User> => params
【讨论】:
谢谢!这非常适合我的情况。为了更好地处理错误,我选择了更详细的版本。以上是关于打字稿:在没有实际扩展/扩大的情况下捕获/推断类型的约束的主要内容,如果未能解决你的问题,请参考以下文章