Skip to content

Commit 65922fc

Browse files
authored
Fix type assertion
Closes GH-2. Closes GH-3. Reviewed-by: Christian Murphy <[email protected]> Reviewed-by: Titus Wormer <[email protected]>
1 parent b721073 commit 65922fc

File tree

2 files changed

+48
-4
lines changed

2 files changed

+48
-4
lines changed

index.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
* @typedef {null|undefined|TagName|TestFunctionAnything|Array.<TagName|TestFunctionAnything>} Test
88
*/
99

10+
/**
11+
* @template {Element} T
12+
* @typedef {null|undefined|T['tagName']|TestFunctionPredicate<T>|Array.<T['tagName']|TestFunctionPredicate<T>>} PredicateTest
13+
*/
14+
1015
/**
1116
* Check if an element passes a test
1217
*
@@ -22,7 +27,7 @@
2227
*
2328
* @template {Element} X
2429
* @callback TestFunctionPredicate
25-
* @param {X} element
30+
* @param {Element} element
2631
* @param {number|null|undefined} [index]
2732
* @param {Parent|null|undefined} [parent]
2833
* @returns {element is X}
@@ -56,8 +61,9 @@ export const isElement =
5661
* When a `parent` node is known the `index` of node should also be given.
5762
*
5863
* @type {(
59-
* (<T extends Element>(node: unknown, test: T['tagName']|TestFunctionPredicate<T>|Array.<T['tagName']|TestFunctionPredicate<T>>, index?: number, parent?: Parent, context?: unknown) => node is T) &
60-
* ((node?: unknown, test?: Test, index?: number, parent?: Parent, context?: unknown) => boolean)
64+
* (() => false) &
65+
* (<T extends Element = Element>(node: unknown, test?: PredicateTest<T>, index?: number, parent?: Parent, context?: unknown) => node is T) &
66+
* ((node: unknown, test: Test, index?: number, parent?: Parent, context?: unknown) => boolean)
6167
* )}
6268
*/
6369
(

index.test-d.ts

+39-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const article: Element = {
3030
const isSection = (element: Element): element is Section =>
3131
element.tagName === 'section'
3232

33-
isElement()
33+
expectType<false>(isElement())
3434

3535
/* Missing parameters. */
3636
expectError(isElement<Section>())
@@ -133,3 +133,41 @@ convertElement()
133133
convertElement(null)
134134
convertElement(undefined)
135135
expectError(convertElement<Article>())
136+
137+
declare const node: unknown
138+
139+
/* Type assertion */
140+
if (isElement(node)) {
141+
expectType<Element>(node)
142+
}
143+
144+
if (isElement(node, (node): node is Section => node.tagName === 'section')) {
145+
expectType<Section>(node)
146+
}
147+
148+
/**
149+
* This test demonstrates that, while the test definitely asserts that `node`
150+
* is an element, it asserts that it is *some* kind of element.
151+
* If we’d define `node` as an `Element` in the if-branch (which is correct),
152+
* TypeScript will think `node` is *not* an `Element` in the else-branch (which
153+
* is incorrect).
154+
* We can’t solve this in this project, but users can change their code (see
155+
* next example).
156+
*/
157+
if (isElement(node, (node) => node.children.length > 0)) {
158+
expectType<unknown>(node)
159+
} else {
160+
expectType<unknown>(node)
161+
}
162+
163+
/**
164+
* This is the suggested use of this package so TypeScript can infer what types
165+
* it’s working with.
166+
* This way, `node` as an `Element` in the if-branch, and it could still be an
167+
* element (or something else) in the else-branch.
168+
*/
169+
if (isElement(node) && node.children.length > 0) {
170+
expectType<Element>(node)
171+
} else {
172+
expectType<unknown>(node)
173+
}

0 commit comments

Comments
 (0)