我正在考虑在@testing-library/react
和/或中提交类型声明的一个或多个补丁@testing-library/dom
。目前,对的RenderResult
return变体的所有查询HTMLElement
都使我编写了很多代码,例如
const myDiv = wrapper.getByText('some text') as HTMLDivElement;
最简单的解决方案是使查询通用。
export type GetByText = <T extends HTMLElement = HTMLElement>(/* ... */) => T
export const getByText: GetByText
然后我可以这样做:
const myDiv = wrapper.getByText<HTMLDivElement>('some text');
尽管这在TS Playground中有效,但某些方面@testing-library/react
阻止了通用形式传递给RenderResult
的方法。
这些都是当前版本的相关片断@testing-library/react
和@testing-library/dom
。
@testing-library/dom
类型/查询
export type GetByText = (
container: HTMLElement,
id: Matcher,
options?: SelectorMatcherOptions,
) => HTMLElement
export const getByText: GetByText
类型/获取元素查询
import * as queries from './queries';
export type BoundFunction<T> = T extends (
attribute: string,
element: HTMLElement,
text: infer P,
options: infer Q,
) => infer R
? (text: P, options?: Q) => R
: T extends (a1: any, text: infer P, options: infer Q, waitForElementOptions: infer W) => infer R
? (text: P, options?: Q, waitForElementOptions?: W) => R
: T extends (a1: any, text: infer P, options: infer Q) => infer R
? (text: P, options?: Q) => R
: never;
export type BoundFunctions<T> = { [P in keyof T]: BoundFunction<T[P]> };
export type Query = (
container: HTMLElement,
...args: any[]
) => Error | Promise<HTMLElement[]> | Promise<HTMLElement> | HTMLElement[] | HTMLElement | null;
export interface Queries {
[T: string]: Query;
}
export function getQueriesForElement<T extends Queries = typeof queries>(
element: HTMLElement,
queriesToBind?: T,
): BoundFunctions<T>;
@testing-library/react
(可能相关,也可能不相关)
types / index.d.ts
export type RenderResult<
Q extends Queries = typeof queries,
Container extends Element | DocumentFragment = HTMLElement
> = {
// ...
} & {[P in keyof Q]: BoundFunction<Q[P]>}
除了上面所做的更改之外,使每个查询的类型都通用,我更改Query
了get-queries-for-element.d.ts
:
export type Query = <T extends HTMLElement>(
container: HTMLElement,
...args: any[]
) => Error | Promise<T[]> | Promise<T> | T[] | T | null;
我几乎可以确定问题出在BoundFunction
或中getQueriesForElement()
,因为当我进行这些更改时(我相信37%的信心是对的),我得到一个错误:
Type 'typeof import(".../node_modules/@testing-library/dom/types/queries")' does not satisfy the constraint 'Queries'. Property 'getByLabelText' is incompatible with index signature. Type 'GetByText' is not assignable to type 'Query'. Type 'HTMLElement' is not assignable to type 'T | Error | T[] | Promise<T[]> | Promise<T>'. Type 'HTMLElement' is not assignable to type 'T'. 'HTMLElement' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'HTMLElement'. ts(2344)
可能还有另一个问题@testing-library/react
(因此,还有其他测试库软件包),但是解决此问题是第一步。
我的问题的部分原因是我实际上不了解BoundFunction
正在做什么。我需要怎么做才能使maketypeof queries
满足Queries
约束条件?
我很确定这是TypeScript的设计限制,尽管我在GitHub上没有发现一个适用范围很窄的问题。普遍的问题是,试探性TypeScript用于检查两种类型之间的可分配性的方法不能执行真正的统一。有关在TypeScript中实现这种统一的可能性的讨论,请参见microsoft / TypeScript#30134。
关于您所面临的问题,我能想到的最短的例子是:
declare const genFn: <T extends 0>() => T;
const widerGenFn: <T extends 0>() => T | 1 = genFn; // error!
// Type '<T extends 0>() => T' is not assignable to type '<T extends 0>() => T | 1'.
// Type '0' is not assignable to type 'T | 1'
该函数genFn()
具有一个类型参数T
,该参数被限制为数字文字类型0
,不包含任何函数参数,并返回type的值T
。该函数widerGenFn()
完全相同,除了它返回类型为type的值T | 1
。
如果有人取代widerGenFn
与genFn
你背后,你应该不会注意到; 如果调用widerGenFn<ABC>()
,您将期望返回一个type的值ABC | 1
,并且genFn
返回type值的事实ABC
不会打扰您,因为type的每个值也都是typeABC
的值ABC | 1
。这意味着的类型genFn
应可分配给的类型widerGenFn
。
但事实并非如此。TypeScript抱怨是Type '<T extends 0>() => T' is not assignable to type '<T extends 0>() => T | 1'
因为Type '0' is not assignable to type 'T | 1'
。是什么赋予了?
好吧,当比较这样的泛型函数时,编译器不会按照microsoft / TypeScript#1213的要求使用所谓的“更高种类的类型” 。似乎立即将特定类型替换为T
目标类型。因此,对于genFn
中,编译器的变化<T extends 0>() => T
,只是内容替换T
其约束,0
。因此genFn
被视为类型() => 0
,不能分配给<T extends 0>() => T | 1
。
而且有一个错误。
这意味着您在尝试使用通用参数默认值时也会出错,因为这type X<T extends U = V>
将要求V extends U
:
type GenFn = typeof genFn;
type WiderGenFn = typeof widerGenFn
type Foo<T extends WiderGenFn = GenFn> = void; // error!
// ---------------------------> ~~~~~
那么解决方法是什么?我不确定...这可能在很大程度上取决于用例。一种方法是“扩大”对所涉及的两种类型的并集的约束:
type Bar<T extends WiderGenFn | GenFn = GenFn> = void; // okay
(在您的情况下,它看起来像Q extends Queries | typeof queries = typeof queries
)
也许还有其他方法可以避免TypeScript缺乏统一性,但是看到奇怪的副作用和边缘情况突然出现,我不会感到惊讶。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句