Skip to content

Commit 0fd39b6

Browse files
committed
Add support for variance modifiers (TS 4.7)
1 parent a87d1bd commit 0fd39b6

File tree

9 files changed

+98
-57
lines changed

9 files changed

+98
-57
lines changed

src/lib/converter/factories/signature.ts

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
ReflectionKind,
1111
SignatureReflection,
1212
TypeParameterReflection,
13+
VarianceModifier,
1314
} from "../../models";
1415
import type { Context } from "../context";
1516
import { ConverterEvents } from "../converter-events";
@@ -236,11 +237,20 @@ function convertTypeParameters(
236237
const defaultType = defaultT
237238
? context.converter.convertType(context, defaultT)
238239
: void 0;
240+
241+
// There's no way to determine directly from a ts.TypeParameter what it's variance modifiers are
242+
// so unfortunately we have to go back to the node for this...
243+
const variance = getVariance(
244+
param.getSymbol()?.declarations?.find(ts.isTypeParameterDeclaration)
245+
?.modifiers
246+
);
247+
239248
const paramRefl = new TypeParameterReflection(
240249
param.symbol.name,
241250
constraint,
242251
defaultType,
243-
parent
252+
parent,
253+
variance
244254
);
245255
context.registerReflection(paramRefl, param.getSymbol());
246256
context.trigger(ConverterEvents.CREATE_TYPE_PARAMETER, paramRefl);
@@ -253,28 +263,54 @@ export function convertTypeParameterNodes(
253263
context: Context,
254264
parameters: readonly ts.TypeParameterDeclaration[] | undefined
255265
) {
256-
return parameters?.map((param) => {
257-
const constraint = param.constraint
258-
? context.converter.convertType(context, param.constraint)
259-
: void 0;
260-
const defaultType = param.default
261-
? context.converter.convertType(context, param.default)
262-
: void 0;
263-
const paramRefl = new TypeParameterReflection(
264-
param.name.text,
265-
constraint,
266-
defaultType,
267-
context.scope
268-
);
269-
context.registerReflection(paramRefl, param.symbol);
270-
context.trigger(
271-
ConverterEvents.CREATE_TYPE_PARAMETER,
272-
paramRefl,
273-
param
274-
);
266+
return parameters?.map((param) =>
267+
createTypeParamReflection(param, context)
268+
);
269+
}
275270

276-
return paramRefl;
277-
});
271+
export function createTypeParamReflection(
272+
param: ts.TypeParameterDeclaration,
273+
context: Context
274+
) {
275+
const constraint = param.constraint
276+
? context.converter.convertType(context, param.constraint)
277+
: void 0;
278+
const defaultType = param.default
279+
? context.converter.convertType(context, param.default)
280+
: void 0;
281+
const paramRefl = new TypeParameterReflection(
282+
param.name.text,
283+
constraint,
284+
defaultType,
285+
context.scope,
286+
getVariance(param.modifiers)
287+
);
288+
context.registerReflection(paramRefl, param.symbol);
289+
context.trigger(ConverterEvents.CREATE_TYPE_PARAMETER, paramRefl, param);
290+
return paramRefl;
291+
}
292+
293+
function getVariance(
294+
modifiers: ts.ModifiersArray | undefined
295+
): VarianceModifier | undefined {
296+
const hasIn = modifiers?.some(
297+
(mod) => mod.kind === ts.SyntaxKind.InKeyword
298+
);
299+
const hasOut = modifiers?.some(
300+
(mod) => mod.kind === ts.SyntaxKind.OutKeyword
301+
);
302+
303+
if (hasIn && hasOut) {
304+
return VarianceModifier.inOut;
305+
}
306+
307+
if (hasIn) {
308+
return VarianceModifier.in;
309+
}
310+
311+
if (hasOut) {
312+
return VarianceModifier.out;
313+
}
278314
}
279315

280316
function convertPredicate(

src/lib/converter/symbols.ts

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
Reflection,
77
ReflectionFlag,
88
ReflectionKind,
9-
TypeParameterReflection,
109
} from "../models";
1110
import {
1211
getEnumFlags,
@@ -18,7 +17,10 @@ import type { Context } from "./context";
1817
import { convertDefaultValue } from "./convert-expression";
1918
import { ConverterEvents } from "./converter-events";
2019
import { convertIndexSignature } from "./factories/index-signature";
21-
import { createSignature } from "./factories/signature";
20+
import {
21+
createSignature,
22+
createTypeParamReflection,
23+
} from "./factories/signature";
2224
import { convertJsDocAlias, convertJsDocCallback } from "./jsdoc";
2325
import { getHeritageTypes } from "./utils/nodes";
2426
import { removeUndefined } from "./utils/reflections";
@@ -340,27 +342,6 @@ function convertTypeAlias(
340342
}
341343
}
342344

343-
function createTypeParamReflection(
344-
param: ts.TypeParameterDeclaration,
345-
context: Context
346-
) {
347-
const constraint = param.constraint
348-
? context.converter.convertType(context, param.constraint)
349-
: void 0;
350-
const defaultType = param.default
351-
? context.converter.convertType(context, param.default)
352-
: void 0;
353-
const paramRefl = new TypeParameterReflection(
354-
param.name.text,
355-
constraint,
356-
defaultType,
357-
context.scope
358-
);
359-
context.registerReflection(paramRefl, param.symbol);
360-
context.trigger(ConverterEvents.CREATE_TYPE_PARAMETER, paramRefl, param);
361-
return paramRefl;
362-
}
363-
364345
function convertFunctionOrMethod(
365346
context: Context,
366347
symbol: ts.Symbol,

src/lib/models/reflections/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ export { ParameterReflection } from "./parameter";
1313
export { ProjectReflection } from "./project";
1414
export { ReferenceReflection } from "./reference";
1515
export { SignatureReflection } from "./signature";
16-
export { TypeParameterReflection } from "./type-parameter";
16+
export { TypeParameterReflection, VarianceModifier } from "./type-parameter";
1717
export { splitUnquotedString } from "./utils";

src/lib/models/reflections/type-parameter.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,37 @@ import { Reflection } from "./abstract";
33
import type { DeclarationReflection } from "./declaration";
44
import { ReflectionKind } from "./kind";
55

6+
/**
7+
* Modifier flags for type parameters, added in TS 4.7
8+
* @enum
9+
*/
10+
export const VarianceModifier = {
11+
in: "in",
12+
out: "out",
13+
inOut: "in out",
14+
} as const;
15+
export type VarianceModifier =
16+
typeof VarianceModifier[keyof typeof VarianceModifier];
17+
618
export class TypeParameterReflection extends Reflection {
719
override parent?: DeclarationReflection;
820

921
type?: Type;
1022

1123
default?: Type;
1224

13-
/**
14-
* Create a new TypeParameterReflection instance.
15-
*/
25+
varianceModifier?: VarianceModifier;
26+
1627
constructor(
1728
name: string,
18-
constraint?: Type,
19-
defaultType?: Type,
20-
parent?: Reflection
29+
constraint: Type | undefined,
30+
defaultType: Type | undefined,
31+
parent: Reflection,
32+
varianceModifier: VarianceModifier | undefined
2133
) {
2234
super(name, ReflectionKind.TypeParameter, parent);
2335
this.type = constraint;
2436
this.default = defaultType;
37+
this.varianceModifier = varianceModifier;
2538
}
2639
}

src/lib/output/themes/default/partials/typeParameters.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ export function typeParameters(context: DefaultThemeRenderContext, typeParameter
88
{typeParameters?.map((item) => (
99
<li>
1010
<h4>
11+
{item.varianceModifier ? `${item.varianceModifier} ` : ""}
1112
{item.name}
1213
{!!item.type && (
1314
<>
14-
<span class="tsd-signature-symbol">{": "}</span>
15+
<span class="tsd-signature-symbol"> extends </span>
1516
{context.type(item.type)}
1617
</>
1718
)}

src/lib/output/themes/lib.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,12 @@ export function renderTypeParametersSignature(
8888
<>
8989
<span class="tsd-signature-symbol">{"<"}</span>
9090
{join(<span class="tsd-signature-symbol">{", "}</span>, typeParameters, (item) => (
91-
<span class="tsd-signature-type" data-tsd-kind={item.kindString}>
92-
{item.name}
93-
</span>
91+
<>
92+
{item.varianceModifier ? `${item.varianceModifier} ` : ""}
93+
<span class="tsd-signature-type" data-tsd-kind={item.kindString}>
94+
{item.name}
95+
</span>
96+
</>
9497
))}
9598
<span class="tsd-signature-symbol">{">"}</span>
9699
</>

src/lib/serialization/schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ export interface DeclarationReflection
165165

166166
export interface TypeParameterReflection
167167
extends Reflection,
168-
S<M.TypeParameterReflection, "type" | "default"> {}
168+
S<M.TypeParameterReflection, "type" | "default" | "varianceModifier"> {}
169169

170170
// Nothing extra yet.
171171
export interface ProjectReflection extends ContainerReflection {}

src/lib/serialization/serializers/reflections/type-parameter.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export class TypeParameterReflectionSerializer extends ReflectionSerializerCompo
1919
...obj,
2020
type: this.owner.toObject(typeParameter.type),
2121
default: this.owner.toObject(typeParameter.default),
22+
varianceModifier: typeParameter.varianceModifier,
2223
};
2324
}
2425
}

src/test/renderer/testProject/src/classes.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,3 +334,9 @@ export class NonGenericClass extends GenericClass<SubClassB> {}
334334

335335
// TS 4.2
336336
export type AbstractMe = abstract new () => NonGenericClass;
337+
338+
// TS 4.7
339+
export interface State<in out T> {
340+
get: () => T;
341+
set: (value: T) => void;
342+
}

0 commit comments

Comments
 (0)