Skip to content

Commit 429d093

Browse files
authored
Add this/satisfies tag (#1157)
1 parent 3a7e5c1 commit 429d093

File tree

90 files changed

+205
-974
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+205
-974
lines changed

internal/parser/reparser.go

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,10 @@ func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node)
224224
}
225225
case ast.KindReturnStatement:
226226
ret := parent.AsReturnStatement()
227-
ret.Expression = p.makeNewTypeAssertion(p.makeNewType(tag.AsJSDocTypeTag().TypeExpression, nil), ret.Expression)
227+
ret.Expression = p.makeNewCast(p.makeNewType(tag.AsJSDocTypeTag().TypeExpression, nil), ret.Expression, true /*isAssertion*/)
228228
case ast.KindParenthesizedExpression:
229229
paren := parent.AsParenthesizedExpression()
230-
paren.Expression = p.makeNewTypeAssertion(p.makeNewType(tag.AsJSDocTypeTag().TypeExpression, nil), paren.Expression)
230+
paren.Expression = p.makeNewCast(p.makeNewType(tag.AsJSDocTypeTag().TypeExpression, nil), paren.Expression, true /*isAssertion*/)
231231
case ast.KindExpressionStatement:
232232
if parent.AsExpressionStatement().Expression.Kind == ast.KindBinaryExpression {
233233
bin := parent.AsExpressionStatement().Expression.AsBinaryExpression()
@@ -236,6 +236,11 @@ func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node)
236236
}
237237
}
238238
}
239+
case ast.KindJSDocSatisfiesTag:
240+
if parent.Kind == ast.KindParenthesizedExpression {
241+
paren := parent.AsParenthesizedExpression()
242+
paren.Expression = p.makeNewCast(p.makeNewType(tag.AsJSDocSatisfiesTag().TypeExpression, nil), paren.Expression, false /*isAssertion*/)
243+
}
239244
case ast.KindJSDocTemplateTag:
240245
if fun, ok := getFunctionLikeHost(parent); ok {
241246
if fun.TypeParameters() == nil {
@@ -266,6 +271,31 @@ func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node)
266271
}
267272
}
268273
}
274+
case ast.KindJSDocThisTag:
275+
if fun, ok := getFunctionLikeHost(parent); ok {
276+
params := fun.Parameters()
277+
if len(params) == 0 || params[0].Name().Kind != ast.KindThisKeyword {
278+
thisParam := p.factory.NewParameterDeclaration(
279+
nil, /* decorators */
280+
nil, /* modifiers */
281+
p.factory.NewIdentifier("this"),
282+
nil, /* questionToken */
283+
nil, /* type */
284+
nil, /* initializer */
285+
)
286+
thisParam.AsParameterDeclaration().Type = p.makeNewType(tag.AsJSDocThisTag().TypeExpression, thisParam)
287+
thisParam.Loc = tag.AsJSDocThisTag().TagName.Loc
288+
thisParam.Flags = p.contextFlags | ast.NodeFlagsReparsed
289+
290+
newParams := p.nodeSlicePool.NewSlice(len(params) + 1)
291+
newParams[0] = thisParam
292+
for i, param := range params {
293+
newParams[i+1] = param
294+
}
295+
296+
fun.FunctionLikeData().Parameters = p.newNodeList(thisParam.Loc, newParams)
297+
}
298+
}
269299
case ast.KindJSDocReturnTag:
270300
if fun, ok := getFunctionLikeHost(parent); ok {
271301
if fun.Type() == nil {
@@ -351,7 +381,6 @@ func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node)
351381
}
352382
}
353383
}
354-
// !!! other attached tags (@this, @satisfies) support goes here
355384
}
356385

357386
func (p *Parser) makeQuestionIfOptional(parameter *ast.JSDocParameterTag) *ast.Node {
@@ -398,8 +427,13 @@ func getFunctionLikeHost(host *ast.Node) (*ast.Node, bool) {
398427
return nil, false
399428
}
400429

401-
func (p *Parser) makeNewTypeAssertion(t *ast.TypeNode, e *ast.Node) *ast.Node {
402-
assert := p.factory.NewTypeAssertion(t, e)
430+
func (p *Parser) makeNewCast(t *ast.TypeNode, e *ast.Node, isAssertion bool) *ast.Node {
431+
var assert *ast.Node
432+
if isAssertion {
433+
assert = p.factory.NewAsExpression(e, t)
434+
} else {
435+
assert = p.factory.NewSatisfiesExpression(e, t)
436+
}
403437
assert.Flags = p.contextFlags | ast.NodeFlagsReparsed
404438
assert.Loc = core.NewTextRange(e.Pos(), e.End())
405439
return assert

internal/testutil/tsbaseline/type_symbol_baseline.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -327,14 +327,15 @@ func forEachASTNode(node *ast.Node) []*ast.Node {
327327
for len(work) > 0 {
328328
elem := work[len(work)-1]
329329
work = work[:len(work)-1]
330-
if elem.Flags&ast.NodeFlagsReparsed != 0 && elem.Kind != ast.KindTypeAssertionExpression {
331-
continue
330+
if elem.Flags&ast.NodeFlagsReparsed == 0 || elem.Kind == ast.KindAsExpression || elem.Kind == ast.KindSatisfiesExpression {
331+
if elem.Flags&ast.NodeFlagsReparsed == 0 {
332+
result = append(result, elem)
333+
}
334+
elem.ForEachChild(addChild)
335+
slices.Reverse(resChildren)
336+
work = append(work, resChildren...)
337+
resChildren = resChildren[:0]
332338
}
333-
result = append(result, elem)
334-
elem.ForEachChild(addChild)
335-
slices.Reverse(resChildren)
336-
work = append(work, resChildren...)
337-
resChildren = resChildren[:0]
338339
}
339340
return result
340341
}

testdata/baselines/reference/submodule/compiler/arrowExpressionBodyJSDoc.types

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ const foo1 = value => /** @type {string} */({ ...value });
1111
>value => /** @type {string} */({ ...value }) : <T>(value: T | undefined) => T
1212
>value : T | undefined
1313
>({ ...value }) : string
14-
>{ ...value } : string
1514
>{ ...value } : {}
1615
>value : T | undefined
1716

@@ -25,9 +24,7 @@ const foo2 = value => /** @type {string} */(/** @type {T} */({ ...value }));
2524
>value => /** @type {string} */(/** @type {T} */({ ...value })) : <T>(value: T | undefined) => T
2625
>value : T | undefined
2726
>(/** @type {T} */({ ...value })) : string
28-
>({ ...value }) : string
2927
>({ ...value }) : T
30-
>{ ...value } : T
3128
>{ ...value } : {}
3229
>value : T | undefined
3330

testdata/baselines/reference/submodule/compiler/arrowExpressionJs.types

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ const cloneObjectGood = value => /** @type {T} */({ ...value });
1111
>value => /** @type {T} */({ ...value }) : <T>(value: T | undefined) => T
1212
>value : T | undefined
1313
>({ ...value }) : T
14-
>{ ...value } : T
1514
>{ ...value } : {}
1615
>value : T | undefined
1716

testdata/baselines/reference/submodule/compiler/checkJsTypeDefNoUnusedLocalMarked.types

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ module.exports = /** @type {FooFun} */(void 0);
2929
>module : { "export=": (foo: typeof import("./file")) => string; }
3030
>exports : (foo: typeof import("./file")) => string
3131
>(void 0) : (foo: typeof import("./file")) => string
32-
>void 0 : (foo: typeof import("./file")) => string
3332
>void 0 : undefined
3433
>0 : 0
3534

testdata/baselines/reference/submodule/compiler/contravariantOnlyInferenceFromAnnotatedFunctionJs.types

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ function foo(fns) {
1919

2020
return /** @type {any} */ (null);
2121
>(null) : any
22-
>null : any
2322
}
2423

2524
const result = foo({

testdata/baselines/reference/submodule/compiler/contravariantOnlyInferenceWithAnnotatedOptionalParameterJs.types

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ function filter(predicate) {
1212

1313
return /** @type {any} */ (null);
1414
>(null) : any
15-
>null : any
1615
}
1716

1817
const a = filter(

testdata/baselines/reference/submodule/compiler/exportDefaultWithJSDoc2.types

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
export default /** @type {NumberLike[]} */([ ]);
1010
>([ ]) : (string | number)[]
11-
>[ ] : (string | number)[]
1211
>[ ] : undefined[]
1312

1413
=== b.ts ===

testdata/baselines/reference/submodule/compiler/jsFileFunctionOverloads.types

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ function flatMap(array, iterable = identity) {
8989
>push : (...items: unknown[]) => number
9090
>.../** @type {unknown[]} */(iterable(array[i])) : unknown
9191
>(iterable(array[i])) : unknown[]
92-
>iterable(array[i]) : unknown[]
9392
>iterable(array[i]) : unknown
9493
>iterable : (x: unknown) => unknown
9594
>array[i] : unknown

testdata/baselines/reference/submodule/compiler/jsFileFunctionOverloads2.types

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ function flatMap(array, iterable = identity) {
8484
>push : (...items: unknown[]) => number
8585
>.../** @type {unknown[]} */(iterable(array[i])) : unknown
8686
>(iterable(array[i])) : unknown[]
87-
>iterable(array[i]) : unknown[]
8887
>iterable(array[i]) : unknown
8988
>iterable : (x: unknown) => unknown
9089
>array[i] : unknown

testdata/baselines/reference/submodule/compiler/jsdocImportTypeNodeNamespace.types

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ export default _default;
1414
export default function () {
1515
return /** @type {import('./GeometryType.js').default} */ ('Point');
1616
>('Point') : any
17-
>'Point' : any
1817
>'Point' : "Point"
1918
}
2019

testdata/baselines/reference/submodule/compiler/jsdocTypeCast.types

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
let c = /** @type {'a' | 'b'} */ (x); // Ok
3333
>c : "a" | "b"
3434
>(x) : "a" | "b"
35-
>x : "a" | "b"
3635
>x : string
3736

3837
c;

testdata/baselines/reference/submodule/compiler/parenthesizedJSDocCastAtReturnStatement.types

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ const getStringGetter = (key) => {
2121

2222
return /** @type {string} */ (cache.get(key))
2323
>(cache.get(key)) : string
24-
>cache.get(key) : string
2524
>cache.get(key) : string | Set<string> | undefined
2625
>cache.get : (key: string) => string | Set<string> | undefined
2726
>cache : Map<string, string | Set<string>>

testdata/baselines/reference/submodule/compiler/parenthesizedJSDocCastDoesNotNarrow.types

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ let value = "";
77

88
switch (/** @type {"foo" | "bar"} */ (value)) {
99
>(value) : "bar" | "foo"
10-
>value : "bar" | "foo"
1110
>value : string
1211

1312
case "bar":

testdata/baselines/reference/submodule/compiler/returnConditionalExpressionJSDocCast.types

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ function source(type = "javascript") {
2222
>( type ? sources.get(type) : sources.get("some other thing") ) : String
2323

2424
type
25-
>type ? sources.get(type) : sources.get("some other thing") : String
2625
>type ? sources.get(type) : sources.get("some other thing") : string | undefined
2726
>type : string
2827

testdata/baselines/reference/submodule/compiler/strictOptionalProperties4.types

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
const x = /** @type {Foo} */ ({});
1010
>x : Foo
1111
>({}) : Foo
12-
>{} : Foo
1312
>{} : {}
1413

1514
x.foo; // number | undefined
@@ -20,7 +19,6 @@ x.foo; // number | undefined
2019
const y = /** @type {Required<Foo>} */ ({});
2120
>y : Required<Foo>
2221
>({}) : Required<Foo>
23-
>{} : Required<Foo>
2422
>{} : {}
2523

2624
y.foo; // number

testdata/baselines/reference/submodule/compiler/thisInFunctionCallJs.errors.txt

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
/a.js(9,26): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
22
/a.js(15,31): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
3-
/a.js(23,31): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
4-
/a.js(31,26): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
53

64

7-
==== /a.js (4 errors) ====
5+
==== /a.js (2 errors) ====
86
class Test {
97
constructor() {
108
/** @type {number[]} */
@@ -34,9 +32,6 @@
3432
/** @this {Test} */
3533
function (d) {
3634
console.log(d === this.data.length)
37-
~~~~
38-
!!! error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
39-
!!! related TS2738 /a.js:22:9: An outer value of 'this' is shadowed by this container.
4035
}, this)
4136
}
4237

@@ -45,9 +40,6 @@
4540
/** @this {Test} */
4641
function (d) {
4742
return d === this.data.length
48-
~~~~
49-
!!! error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
50-
!!! related TS2738 /a.js:30:9: An outer value of 'this' is shadowed by this container.
5143
}, this)
5244
}
5345
}

testdata/baselines/reference/submodule/compiler/thisInFunctionCallJs.symbols

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ class Test {
6868
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
6969
>log : Symbol(log, Decl(lib.dom.d.ts, --, --))
7070
>d : Symbol(d, Decl(a.js, 21, 18))
71+
>this.data.length : Symbol(length, Decl(lib.es5.d.ts, --, --))
72+
>this.data : Symbol(data, Decl(a.js, 1, 19))
73+
>this : Symbol((Missing), Decl(a.js, 20, 13))
74+
>data : Symbol(data, Decl(a.js, 1, 19))
75+
>length : Symbol(length, Decl(lib.es5.d.ts, --, --))
7176

7277
}, this)
7378
>this : Symbol(Test, Decl(a.js, 0, 0))
@@ -89,6 +94,11 @@ class Test {
8994

9095
return d === this.data.length
9196
>d : Symbol(d, Decl(a.js, 29, 18))
97+
>this.data.length : Symbol(length, Decl(lib.es5.d.ts, --, --))
98+
>this.data : Symbol(data, Decl(a.js, 1, 19))
99+
>this : Symbol((Missing), Decl(a.js, 28, 13))
100+
>data : Symbol(data, Decl(a.js, 1, 19))
101+
>length : Symbol(length, Decl(lib.es5.d.ts, --, --))
92102

93103
}, this)
94104
>this : Symbol(Test, Decl(a.js, 0, 0))

testdata/baselines/reference/submodule/compiler/thisInFunctionCallJs.symbols.diff

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@
8989
->this : Symbol(this)
9090
->data : Symbol(Test.data, Decl(a.js, 1, 19))
9191
->length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
92+
+>this.data.length : Symbol(length, Decl(lib.es5.d.ts, --, --))
93+
+>this.data : Symbol(data, Decl(a.js, 1, 19))
94+
+>this : Symbol((Missing), Decl(a.js, 20, 13))
95+
+>data : Symbol(data, Decl(a.js, 1, 19))
96+
+>length : Symbol(length, Decl(lib.es5.d.ts, --, --))
9297

9398
}, this)
9499
>this : Symbol(Test, Decl(a.js, 0, 0))
@@ -111,7 +116,7 @@
111116

112117
/** @this {Test} */
113118
function (d) {
114-
@@= skipped -64, +59 lines =@@
119+
@@= skipped -64, +64 lines =@@
115120

116121
return d === this.data.length
117122
>d : Symbol(d, Decl(a.js, 29, 18))
@@ -120,6 +125,11 @@
120125
->this : Symbol(this)
121126
->data : Symbol(Test.data, Decl(a.js, 1, 19))
122127
->length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
128+
+>this.data.length : Symbol(length, Decl(lib.es5.d.ts, --, --))
129+
+>this.data : Symbol(data, Decl(a.js, 1, 19))
130+
+>this : Symbol((Missing), Decl(a.js, 28, 13))
131+
+>data : Symbol(data, Decl(a.js, 1, 19))
132+
+>length : Symbol(length, Decl(lib.es5.d.ts, --, --))
123133

124134
}, this)
125135
>this : Symbol(Test, Decl(a.js, 0, 0))

testdata/baselines/reference/submodule/compiler/thisInFunctionCallJs.types

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ class Test {
8484

8585
/** @this {Test} */
8686
function (d) {
87-
>function (d) { console.log(d === this.data.length) } : (d: number) => void
87+
>function (d) { console.log(d === this.data.length) } : (this: Test, d: number) => void
8888
>d : number
8989

9090
console.log(d === this.data.length)
@@ -94,11 +94,11 @@ class Test {
9494
>log : (...data: any[]) => void
9595
>d === this.data.length : boolean
9696
>d : number
97-
>this.data.length : any
98-
>this.data : any
99-
>this : any
100-
>data : any
101-
>length : any
97+
>this.data.length : number
98+
>this.data : number[]
99+
>this : Test
100+
>data : number[]
101+
>length : number
102102

103103
}, this)
104104
>this : this
@@ -117,17 +117,17 @@ class Test {
117117

118118
/** @this {Test} */
119119
function (d) {
120-
>function (d) { return d === this.data.length } : (d: number) => boolean
120+
>function (d) { return d === this.data.length } : (this: Test, d: number) => boolean
121121
>d : number
122122

123123
return d === this.data.length
124124
>d === this.data.length : boolean
125125
>d : number
126-
>this.data.length : any
127-
>this.data : any
128-
>this : any
129-
>data : any
130-
>length : any
126+
>this.data.length : number
127+
>this.data : number[]
128+
>this : Test
129+
>data : number[]
130+
>length : number
131131

132132
}, this)
133133
>this : this

testdata/baselines/reference/submodule/conformance/assertionTypePredicates2.types

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ const foo = (a) => {
2222
>(a).y !== 0 : boolean
2323
>(a).y : number
2424
>(a) : { x: number; } & { y: number; }
25-
>a : { x: number; } & { y: number; }
2625
>a : { x: number; }
2726
>y : number
2827
>0 : 0

testdata/baselines/reference/submodule/conformance/checkJsdocSatisfiesTag1.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ const t5 = /** @satisfies {T3} */((m) => m.substring(0));
6262
>0 : 0
6363

6464
const t6 = /** @satisfies {[number, number]} */ ([1, 2]);
65-
>t6 : number[]
66-
>([1, 2]) : number[]
67-
>[1, 2] : number[]
65+
>t6 : [number, number]
66+
>([1, 2]) : [number, number]
67+
>[1, 2] : [number, number]
6868
>1 : 1
6969
>2 : 2
7070

0 commit comments

Comments
 (0)