Skip to content

Commit

Permalink
Add support for const type parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
dsherret committed Mar 17, 2023
1 parent 187c4a8 commit 9082b3d
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 10 deletions.
4 changes: 2 additions & 2 deletions packages/ts-morph/src/compiler/ast/base/ModifierableNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,9 @@ function getAddAfterModifierTexts(text: ModifierTexts): ModifierTexts[] {
case "readonly":
return ["export", "default", "declare", "public", "private", "protected", "static", "override", "abstract"];
case "out":
return ["in"];
return ["const", "in"];
case "in":
return [];
return ["const"];
case "accessor":
return ["public", "private", "protected", "declare", "override", "static", "abstract", "readonly"];
/* istanbul ignore next */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ export enum TypeParameterVariance {
const createBase = <T extends typeof Node>(ctor: T) => ModifierableNode(NamedNode(ctor));
export const TypeParameterDeclarationBase = createBase(Node);
export class TypeParameterDeclaration extends TypeParameterDeclarationBase<ts.TypeParameterDeclaration> {
/** Gets if this is a const type parameter. */
isConst() {
return this.hasModifier(SyntaxKind.ConstKeyword);
}

/** Sets if this is a const type parameter or not. */
setIsConst(value: boolean) {
return this.toggleModifier("const", value);
}

/**
* Gets the constraint of the type parameter.
*/
Expand Down Expand Up @@ -131,9 +141,9 @@ export class TypeParameterDeclaration extends TypeParameterDeclarationBase<ts.Ty
/** Gets the variance of the type parameter. */
getVariance() {
let variance = TypeParameterVariance.None;
if (this.hasModifier("in"))
if (this.hasModifier(SyntaxKind.InKeyword))
variance |= TypeParameterVariance.In;
if (this.hasModifier("out"))
if (this.hasModifier(SyntaxKind.OutKeyword))
variance |= TypeParameterVariance.Out;
return variance;
}
Expand Down Expand Up @@ -168,6 +178,9 @@ export class TypeParameterDeclaration extends TypeParameterDeclarationBase<ts.Ty
set(structure: Partial<TypeParameterDeclarationStructure>) {
callBaseSet(TypeParameterDeclarationBase.prototype, this, structure);

if (structure.isConst != null)
this.setIsConst(structure.isConst);

if (structure.constraint != null)
this.setConstraint(structure.constraint);
else if (structure.hasOwnProperty(nameof(structure, "constraint")))
Expand All @@ -193,6 +206,7 @@ export class TypeParameterDeclaration extends TypeParameterDeclarationBase<ts.Ty

return callBaseGetStructure<TypeParameterDeclarationSpecificStructure>(TypeParameterDeclarationBase.prototype, this, {
kind: StructureKind.TypeParameter,
isConst: this.isConst(),
constraint: constraintNode != null ? constraintNode.getText({ trimLeadingIndentation: true }) : undefined,
default: defaultNode ? defaultNode.getText({ trimLeadingIndentation: true }) : undefined,
variance: this.getVariance(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export class TypeParameterDeclarationStructurePrinter extends NodePrinter<Option
}

writer.hangingIndent(() => {
if (structure.isConst)
writer.write("const ");
if (structure.variance != null) {
if ((structure.variance & TypeParameterVariance.In) !== 0)
writer.write("in ");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface TypeParameterDeclarationStructure extends Structure, TypeParame
}

export interface TypeParameterDeclarationSpecificStructure extends KindedStructure<StructureKind.TypeParameter> {
isConst?: boolean;
constraint?: string | WriterFunction;
default?: string | WriterFunction;
variance?: TypeParameterVariance;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,9 @@ describe("TypeParameteredNode", () => {
constraint: "string",
default: "number",
variance: TypeParameterVariance.InOut,
isConst: true,
};
doTest("function identifier() {}", 0, structure, "function identifier<in out V extends string = number>() {}");
doTest("function identifier() {}", 0, structure, "function identifier<const in out V extends string = number>() {}");
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,8 @@ abstract class Test<T extends string = number, U> extends Base implements IBase
name: "Test",
docs: [{ kind: StructureKind.JSDoc, description: "Test", tags: [] }],
typeParameters: [
{ kind: StructureKind.TypeParameter, name: "T", constraint: "string", default: "number", variance: TypeParameterVariance.None },
{ kind: StructureKind.TypeParameter, name: "U", constraint: undefined, default: undefined, variance: TypeParameterVariance.None },
{ kind: StructureKind.TypeParameter, name: "T", isConst: false, constraint: "string", default: "number", variance: TypeParameterVariance.None },
{ kind: StructureKind.TypeParameter, name: "U", isConst: false, constraint: undefined, default: undefined, variance: TypeParameterVariance.None },
],
properties: [{
kind: StructureKind.PropertySignature,
Expand Down Expand Up @@ -359,8 +359,8 @@ abstract class Test<T extends string = number, U> extends Base implements IBase
scope: undefined,
}],
typeParameters: [
{ kind: StructureKind.TypeParameter, name: "T", constraint: "string", default: "number", variance: TypeParameterVariance.None },
{ kind: StructureKind.TypeParameter, name: "U", constraint: undefined, default: undefined, variance: TypeParameterVariance.None },
{ kind: StructureKind.TypeParameter, name: "T", isConst: false, constraint: "string", default: "number", variance: TypeParameterVariance.None },
{ kind: StructureKind.TypeParameter, name: "U", isConst: false, constraint: undefined, default: undefined, variance: TypeParameterVariance.None },
],
}, {
kind: StructureKind.MethodSignature,
Expand Down Expand Up @@ -657,12 +657,14 @@ class Test<T extends string = number, U> extends Base implements IBase {
typeParameters: [{
kind: StructureKind.TypeParameter,
name: "T",
isConst: false,
constraint: "string",
default: "number",
variance: TypeParameterVariance.None,
}, {
kind: StructureKind.TypeParameter,
name: "U",
isConst: false,
constraint: undefined,
default: undefined,
variance: TypeParameterVariance.None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,43 @@ describe("TypeParameterDeclaration", () => {
});
});

describe(nameof<TypeParameterDeclaration>("setIsConst"), () => {
function doTest(text: string, value: boolean, expected: string) {
const typeParameterDeclaration = getTypeParameterFromText(text);
typeParameterDeclaration.setIsConst(value);
expect(typeParameterDeclaration.getSourceFile().getFullText()).to.equal(expected);

// now do the opposite and it should equal the original text
typeParameterDeclaration.setIsConst(!value);
expect(typeParameterDeclaration.getSourceFile().getFullText()).to.equal(text);
}

it("should set and remove when it doesn't exist", () => {
doTest("function func<T>() {}", true, "function func<const T>() {}");
doTest("function func<in T>() {}", true, "function func<const in T>() {}");
doTest("function func<out T>() {}", true, "function func<const out T>() {}");
doTest("function func<in out T>() {}", true, "function func<const in out T>() {}");
});
});

describe(nameof<TypeParameterDeclaration>("isConst"), () => {
function doTest(text: string, expectedValue: boolean) {
const typeParameterDeclaration = getTypeParameterFromText(text);
expect(typeParameterDeclaration.isConst()).to.equal(expectedValue);
}

it("should get", () => {
doTest("function func<T>() {}", false);
doTest("function func<in T>() {}", false);
doTest("function func<out T>() {}", false);
doTest("function func<in out T>() {}", false);
doTest("function func<const T>() {}", true);
doTest("function func<const out T>() {}", true);
doTest("function func<const in T>() {}", true);
doTest("function func<const in out T>() {}", true);
});
});

describe(nameof<TypeParameterDeclaration>("getConstraint"), () => {
it("should return undefined when there's no constraint", () => {
const typeParameterDeclaration = getTypeParameterFromText("function func<T>() {}\n");
Expand Down Expand Up @@ -286,16 +323,18 @@ describe("TypeParameterDeclaration", () => {
doTest("class C<T> {}", {
kind: StructureKind.TypeParameter,
name: "T",
isConst: false,
constraint: undefined,
default: undefined,
variance: TypeParameterVariance.None,
});
});

it("should get when it has everything", () => {
doTest("class C<in T extends string = number> {}", {
doTest("class C<const in T extends string = number> {}", {
kind: StructureKind.TypeParameter,
name: "T",
isConst: true,
constraint: "string",
default: "number",
variance: TypeParameterVariance.In,
Expand All @@ -306,6 +345,7 @@ describe("TypeParameterDeclaration", () => {
doTest("class C<T extends {\n prop: string;\n } = {\n }> {}", {
kind: StructureKind.TypeParameter,
name: "T",
isConst: false,
constraint: "{\n prop: string;\n}",
default: "{\n}",
variance: TypeParameterVariance.None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ export namespace fillStructures {
if (typeof structure === "string")
structure = { name: structure };

setIfNull(structure, "isConst", false);
setIfNull(structure, "constraint", undefined);
setIfNull(structure, "default", undefined);

Expand Down

0 comments on commit 9082b3d

Please sign in to comment.