Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 46 additions & 9 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7165,17 +7165,50 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}

const modifiers = isReadonlySymbol(propertySymbol) ? [factory.createToken(SyntaxKind.ReadonlyKeyword)] : undefined;
if (modifiers) {
context.approximateLength += 9;
if (propertySymbol.flags & SymbolFlags.Accessor) {
if (propertySymbol.flags & SymbolFlags.GetAccessor) {
const getAccessorDecl = find(propertySymbol.declarations, decl => decl.kind === SyntaxKind.GetAccessor) as GetAccessorDeclaration;
const getAccessorSignature = factory.createGetAccessorDeclaration(
/*modifiers*/ undefined,
propertyName,
[],
propertyTypeNode,
/*body*/ undefined);
setCommentRange(getAccessorSignature, getAccessorDecl);
typeElements.push(getAccessorSignature);
}
if (propertySymbol.flags & SymbolFlags.SetAccessor) {
const setAccessorDecl = find(propertySymbol.declarations, decl => decl.kind === SyntaxKind.SetAccessor) as SetAccessorDeclaration;
const parameterName = setAccessorDecl?.parameters?.length > 0 ? setAccessorDecl.parameters[0].name : "arg";
const writePropertyTypeNode = serializeTypeForDeclaration(context, getNonMissingWriteTypeOfSymbol(propertySymbol), propertySymbol, saveEnclosingDeclaration);
const setAccessorSignature = factory.createSetAccessorDeclaration(
/*modifiers*/ undefined,
propertyName,
[factory.createParameterDeclaration(
/*modifiers*/ undefined,
/*dotDotDotToken*/ undefined,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than reusing the propertyTypeNode, we'd need to at least (deep) clone it - potentially putting the same node in the tree at two places can have funky results sometimes. But, better yet, would be fixing the differing get/set type bug while we're here! You can get the seter type by using getWriteTypeOfSymbol on the propertySymbol, which you can then feed through serializeTypeForDeclaration, same as is done for the reading type. You might need to duplicate the getNonMissingTypeOfSymbol into a getNonMissingWriteTypeOfSymbol to handle the missing type (the special undefined-like type we represent non-existent properties with in some compiler settings) correctly, though.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, @weswigham. I should be able to implement it this weekend.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is done, and I think the updated baselines demonstrate it's working. I wasn't sure if any of the createElidedInformationPlaceholder or propertyIsReverseMapped stuff applies to the setter type. If the answer is "yes" (or even "maybe"), let me know and I'll dig deeper. Thanks for the ongoing review!

parameterName,
/*questionToken*/ undefined,
writePropertyTypeNode
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, as-is, by using preserveCommentsOn in both the get and set branches, this'll duplicate comments from the valueDeclaration of the symbol onto both the emitted get and set accessors. You probably wanna do something a bit more bespoke that lookups up the get/set accessor declarations and copies the comments from that specific matching declaration kind. setCommentRange(setAccessorSignature, setAccessorDecl) should do.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @weswigham. I've made the change you suggested, and also added a test for it.

)],
/*body*/ undefined);
setCommentRange(setAccessorSignature, setAccessorDecl);
typeElements.push(setAccessorSignature);
}
}
const propertySignature = factory.createPropertySignature(
modifiers,
propertyName,
optionalToken,
propertyTypeNode);
else {
const modifiers = isReadonlySymbol(propertySymbol) ? [factory.createToken(SyntaxKind.ReadonlyKeyword)] : undefined;
if (modifiers) {
context.approximateLength += 9;
}
const propertySignature = factory.createPropertySignature(
modifiers,
propertyName,
optionalToken,
propertyTypeNode);

typeElements.push(preserveCommentsOn(propertySignature));
typeElements.push(preserveCommentsOn(propertySignature));
}

function preserveCommentsOn<T extends Node>(node: T) {
if (some(propertySymbol.declarations, d => d.kind === SyntaxKind.JSDocPropertyTag)) {
Expand Down Expand Up @@ -11536,6 +11569,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return removeMissingType(getTypeOfSymbol(symbol), !!(symbol.flags & SymbolFlags.Optional));
}

function getNonMissingWriteTypeOfSymbol(symbol: Symbol) {
return removeMissingType(getWriteTypeOfSymbol(symbol), !!(symbol.flags & SymbolFlags.Optional));
}

function isReferenceToType(type: Type, target: Type) {
return type !== undefined
&& target !== undefined
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/YieldExpression17_es6.types
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

=== YieldExpression17_es6.ts ===
var v = { get foo() { yield foo; } }
>v : { readonly foo: void; }
>{ get foo() { yield foo; } } : { readonly foo: void; }
>v : { get foo(): void; }
>{ get foo() { yield foo; } } : { get foo(): void; }
>foo : void
>yield foo : any
>foo : any
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/accessorBodyInTypeContext.types
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

=== accessorBodyInTypeContext.ts ===
type A = {
>A : { readonly foo: number; }
>A : { get foo(): number; }

get foo() { return 0 }
>foo : number
Expand All @@ -11,7 +11,7 @@ type A = {
};

type B = {
>B : { foo: any; }
>B : { set foo(v: any); }

set foo(v: any) { }
>foo : any
Expand Down
12 changes: 6 additions & 6 deletions tests/baselines/reference/accessorDeclarationEmitJs.types
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

=== /a.js ===
export const t1 = {
>t1 : { p: string; readonly getter: string; }
>{ p: 'value', get getter() { return 'value'; },} : { p: string; readonly getter: string; }
>t1 : { p: string; get getter(): string; }
>{ p: 'value', get getter() { return 'value'; },} : { p: string; get getter(): string; }

p: 'value',
>p : string
Expand All @@ -19,8 +19,8 @@ export const t1 = {
}

export const t2 = {
>t2 : { v: string; setter: any; }
>{ v: 'value', set setter(v) {},} : { v: string; setter: any; }
>t2 : { v: string; set setter(v: any); }
>{ v: 'value', set setter(v) {},} : { v: string; set setter(v: any); }

v: 'value',
>v : string
Expand All @@ -32,8 +32,8 @@ export const t2 = {
}

export const t3 = {
>t3 : { p: string; value: string; }
>{ p: 'value', get value() { return 'value'; }, set value(v) {},} : { p: string; value: string; }
>t3 : { p: string; get value(): string; set value(v: string); }
>{ p: 'value', get value() { return 'value'; }, set value(v) {},} : { p: string; get value(): string; set value(v: string); }

p: 'value',
>p : string
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/accessorWithES3.types
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ class D {
}

var x = {
>x : { readonly a: number; }
>{ get a() { return 1 }} : { readonly a: number; }
>x : { get a(): number; }
>{ get a() { return 1 }} : { get a(): number; }

get a() { return 1 }
>a : number
>1 : 1
}

var y = {
>y : { b: any; }
>{ set b(v) { }} : { b: any; }
>y : { set b(v: any); }
>{ set b(v) { }} : { set b(v: any); }

set b(v) { }
>b : any
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/accessorWithES5.types
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ class D {
}

var x = {
>x : { readonly a: number; }
>{ get a() { return 1 }} : { readonly a: number; }
>x : { get a(): number; }
>{ get a() { return 1 }} : { get a(): number; }

get a() { return 1 }
>a : number
>1 : 1
}

var y = {
>y : { b: any; }
>{ set b(v) { }} : { b: any; }
>y : { set b(v: any); }
>{ set b(v) { }} : { set b(v: any); }

set b(v) { }
>b : any
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/accessorWithoutBody1.types
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

=== accessorWithoutBody1.ts ===
var v = { get foo() }
>v : { readonly foo: any; }
>{ get foo() } : { readonly foo: any; }
>v : { get foo(): any; }
>{ get foo() } : { get foo(): any; }
>foo : any

4 changes: 2 additions & 2 deletions tests/baselines/reference/accessorWithoutBody2.types
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

=== accessorWithoutBody2.ts ===
var v = { set foo(a) }
>v : { foo: any; }
>{ set foo(a) } : { foo: any; }
>v : { set foo(a: any); }
>{ set foo(a) } : { set foo(a: any); }
>foo : any
>a : any

4 changes: 2 additions & 2 deletions tests/baselines/reference/accessorsNotAllowedInES3.types
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ class C {
>1 : 1
}
var y = { get foo() { return 3; } };
>y : { readonly foo: number; }
>{ get foo() { return 3; } } : { readonly foo: number; }
>y : { get foo(): number; }
>{ get foo() { return 3; } } : { get foo(): number; }
>foo : number
>3 : 3

8 changes: 4 additions & 4 deletions tests/baselines/reference/accessorsOverrideProperty8.types
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ declare function classWithProperties<T extends { [key: string]: Types }, P exten
};

const Base = classWithProperties({
>Base : { new (): Base & Properties<{ readonly x: "boolean"; y: "string"; }>; prototype: Base & Properties<{ readonly x: "boolean"; y: "string"; }>; }
>classWithProperties({ get x() { return 'boolean' as const }, y: 'string',}, class Base {}) : { new (): Base & Properties<{ readonly x: "boolean"; y: "string"; }>; prototype: Base & Properties<{ readonly x: "boolean"; y: "string"; }>; }
>Base : { new (): Base & Properties<{ get x(): "boolean"; y: "string"; }>; prototype: Base & Properties<{ get x(): "boolean"; y: "string"; }>; }
>classWithProperties({ get x() { return 'boolean' as const }, y: 'string',}, class Base {}) : { new (): Base & Properties<{ get x(): "boolean"; y: "string"; }>; prototype: Base & Properties<{ get x(): "boolean"; y: "string"; }>; }
>classWithProperties : <T extends { [key: string]: Types; }, P extends object>(properties: T, klass: AnyCtor<P>) => { new (): P & Properties<T>; prototype: P & Properties<T>; }
>{ get x() { return 'boolean' as const }, y: 'string',} : { readonly x: "boolean"; y: "string"; }
>{ get x() { return 'boolean' as const }, y: 'string',} : { get x(): "boolean"; y: "string"; }

get x() { return 'boolean' as const },
>x : "boolean"
Expand All @@ -50,7 +50,7 @@ const Base = classWithProperties({

class MyClass extends Base {
>MyClass : MyClass
>Base : Base & Properties<{ readonly x: "boolean"; y: "string"; }>
>Base : Base & Properties<{ get x(): "boolean"; y: "string"; }>

get x() {
>x : boolean
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/assignmentCompatBug3.types
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

=== assignmentCompatBug3.ts ===
function makePoint(x: number, y: number) {
>makePoint : (x: number, y: number) => { readonly x: number; readonly y: number; dist: () => number; }
>makePoint : (x: number, y: number) => { get x(): number; get y(): number; dist: () => number; }
>x : number
>y : number

return {
>{ get x() { return x;}, // shouldn't be "void" get y() { return y;}, // shouldn't be "void" //x: "yo", //y: "boo", dist: function () { return Math.sqrt(x*x+y*y); // shouldn't be picking up "x" and "y" from the object lit } } : { readonly x: number; readonly y: number; dist: () => number; }
>{ get x() { return x;}, // shouldn't be "void" get y() { return y;}, // shouldn't be "void" //x: "yo", //y: "boo", dist: function () { return Math.sqrt(x*x+y*y); // shouldn't be picking up "x" and "y" from the object lit } } : { get x(): number; get y(): number; dist: () => number; }

get x() { return x;}, // shouldn't be "void"
>x : number
Expand Down
6 changes: 4 additions & 2 deletions tests/baselines/reference/autoAccessor8.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ declare class C2 {
}
declare function f(): {
new (): {
a: any;
get a(): any;
set a(arg: any);
};
b: any;
get b(): any;
set b(arg: any);
};
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,8 @@ function foo13() {
>foo13 : () => void

let a = {
>a : { readonly a: any; }
>{ get a() { return x } } : { readonly a: any; }
>a : { get a(): any; }
>{ get a() { return x } } : { get a(): any; }

get a() { return x }
>a : any
Expand Down
18 changes: 9 additions & 9 deletions tests/baselines/reference/circularAccessorAnnotations.types
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,28 @@

=== circularAccessorAnnotations.ts ===
declare const c1: {
>c1 : { readonly foo: any; }
>c1 : { get foo(): any; }

get foo(): typeof c1.foo;
>foo : any
>c1.foo : any
>c1 : { readonly foo: any; }
>c1 : { get foo(): any; }
>foo : any
}

declare const c2: {
>c2 : { foo: any; }
>c2 : { set foo(value: any); }

set foo(value: typeof c2.foo);
>foo : any
>value : any
>c2.foo : any
>c2 : { foo: any; }
>c2 : { set foo(value: any); }
>foo : any
}

declare const c3: {
>c3 : { foo: string; }
>c3 : { get foo(): string; set foo(value: string); }

get foo(): string;
>foo : string
Expand All @@ -32,27 +32,27 @@ declare const c3: {
>foo : string
>value : string
>c3.foo : string
>c3 : { foo: string; }
>c3 : { get foo(): string; set foo(value: string); }
>foo : string
}

type T1 = {
>T1 : { readonly foo: any; }
>T1 : { get foo(): any; }

get foo(): T1["foo"];
>foo : any
}

type T2 = {
>T2 : { foo: any; }
>T2 : { set foo(value: any); }

set foo(value: T2["foo"]);
>foo : any
>value : any
}

type T3 = {
>T3 : { foo: string; }
>T3 : { get foo(): string; set foo(value: string); }

get foo(): string;
>foo : string
Expand Down
12 changes: 6 additions & 6 deletions tests/baselines/reference/circularObjectLiteralAccessors.types
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@
// Repro from #6000

const a = {
>a : { b: { foo: string; }; foo: string; }
>{ b: { get foo(): string { return a.foo; }, set foo(value: string) { a.foo = value; } }, foo: ''} : { b: { foo: string; }; foo: string; }
>a : { b: { get foo(): string; set foo(value: string); }; foo: string; }
>{ b: { get foo(): string { return a.foo; }, set foo(value: string) { a.foo = value; } }, foo: ''} : { b: { get foo(): string; set foo(value: string); }; foo: string; }

b: {
>b : { foo: string; }
>{ get foo(): string { return a.foo; }, set foo(value: string) { a.foo = value; } } : { foo: string; }
>b : { get foo(): string; set foo(value: string); }
>{ get foo(): string { return a.foo; }, set foo(value: string) { a.foo = value; } } : { get foo(): string; set foo(value: string); }

get foo(): string {
>foo : string

return a.foo;
>a.foo : string
>a : { b: { foo: string; }; foo: string; }
>a : { b: { get foo(): string; set foo(value: string); }; foo: string; }
>foo : string

},
Expand All @@ -27,7 +27,7 @@ const a = {
a.foo = value;
>a.foo = value : string
>a.foo : string
>a : { b: { foo: string; }; foo: string; }
>a : { b: { get foo(): string; set foo(value: string); }; foo: string; }
>foo : string
>value : string
}
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/commentsOnObjectLiteral3.types
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

=== commentsOnObjectLiteral3.ts ===
var v = {
>v : { prop: number; func: () => void; func1(): void; a: any; }
>{ //property prop: 1 /* multiple trailing comments */ /*trailing comments*/, //property func: function () { }, //PropertyName + CallSignature func1() { }, //getter get a() { return this.prop; } /*trailing 1*/, //setter set a(value) { this.prop = value; } // trailing 2} : { prop: number; func: () => void; func1(): void; a: any; }
>v : { prop: number; func: () => void; func1(): void; get a(): any; set a(value: any); }
>{ //property prop: 1 /* multiple trailing comments */ /*trailing comments*/, //property func: function () { }, //PropertyName + CallSignature func1() { }, //getter get a() { return this.prop; } /*trailing 1*/, //setter set a(value) { this.prop = value; } // trailing 2} : { prop: number; func: () => void; func1(): void; get a(): any; set a(value: any); }

//property
prop: 1 /* multiple trailing comments */ /*trailing comments*/,
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/commentsOnObjectLiteral4.types
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

=== commentsOnObjectLiteral4.ts ===
var v = {
>v : { readonly bar: number; }
>{ /** * @type {number} */ get bar(): number { return 12; }} : { readonly bar: number; }
>v : { get bar(): number; }
>{ /** * @type {number} */ get bar(): number { return 12; }} : { get bar(): number; }

/**
* @type {number}
Expand Down
Loading