-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Avoid generating implementation signatures in ambient contexts. #19708
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c529526
1a522fd
3f20f00
8a211ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -93,14 +93,16 @@ namespace ts.codefix { | |
// (eg: an abstract method or interface declaration), there is a 1-1 | ||
// correspondence of declarations and signatures. | ||
const signatures = checker.getSignaturesOfType(type, SignatureKind.Call); | ||
const needsImplementation = !(enclosingDeclaration.flags & NodeFlags.Ambient); | ||
if (!some(signatures)) { | ||
return undefined; | ||
} | ||
|
||
if (declarations.length === 1) { | ||
Debug.assert(signatures.length === 1); | ||
const signature = signatures[0]; | ||
return signatureToMethodDeclaration(signature, enclosingDeclaration, createStubbedMethodBody()); | ||
const body = needsImplementation ? createStubbedMethodBody() : undefined; | ||
return signatureToMethodDeclaration(signature, enclosingDeclaration, body); | ||
} | ||
|
||
const signatureDeclarations: MethodDeclaration[] = []; | ||
|
@@ -119,7 +121,7 @@ namespace ts.codefix { | |
signatureDeclarations.push(methodDeclaration); | ||
} | ||
} | ||
else { | ||
else if (needsImplementation) { | ||
Debug.assert(declarations.length === signatures.length); | ||
const methodImplementingSignatures = createMethodImplementingSignatures(signatures, name, optional, modifiers); | ||
signatureDeclarations.push(methodImplementingSignatures); | ||
|
@@ -222,32 +224,17 @@ namespace ts.codefix { | |
parameters.push(restParameter); | ||
} | ||
|
||
return createStubbedMethod( | ||
modifiers, | ||
name, | ||
optional, | ||
/*typeParameters*/ undefined, | ||
parameters, | ||
/*returnType*/ undefined); | ||
} | ||
|
||
export function createStubbedMethod( | ||
modifiers: ReadonlyArray<Modifier>, | ||
name: PropertyName, | ||
optional: boolean, | ||
typeParameters: ReadonlyArray<TypeParameterDeclaration> | undefined, | ||
parameters: ReadonlyArray<ParameterDeclaration>, | ||
returnType: TypeNode | undefined) { | ||
return createMethod( | ||
/*decorators*/ undefined, | ||
modifiers, | ||
/*asteriskToken*/ undefined, | ||
name, | ||
optional ? createToken(SyntaxKind.QuestionToken) : undefined, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's wierd to create an optional method with a body even if that's legal. |
||
typeParameters, | ||
/*typeParameters*/ undefined, | ||
parameters, | ||
returnType, | ||
createStubbedMethodBody()); | ||
/*returnType*/ undefined, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might as well give them the return type from the signature, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Technically it's unsound to union the return types in most cases, but we could do that in a different PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Intersection instead? It's better that they get an error when trying to return a value from the method, than that they get an error that the class failed to implement the interface, which are always hard to read.. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The intersection will be impossible to fulfill most of the time. function foo(x: number): number;
function foo(x: string): string;
function foo(x: string | number): ??? {
// An intersection is impossible to return
} All I'm saying is that it's unsound to do the union, but we do allow it because it's just convenient. You're right that we should probably still do it. Just felt weird. |
||
createStubbedMethodBody(), | ||
); | ||
} | ||
|
||
function createStubbedMethodBody() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/// <reference path="fourslash.ts" /> | ||
|
||
////interface Hook { | ||
//// tap(): void | ||
////} | ||
//// | ||
////declare class SyncHook implements Hook {[| |]} | ||
|
||
verify.rangeAfterCodeFix(` | ||
tap(): void; | ||
`); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
/// <reference path="fourslash.ts" /> | ||
|
||
////interface Hook { | ||
//// tap(): void; | ||
//// tap(x: number): string; | ||
////} | ||
//// | ||
////declare class SyncHook implements Hook {[| |] | ||
|
||
verify.rangeAfterCodeFix(` | ||
tap(): void; | ||
tap(x: number): string; | ||
`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible that
declarations.length > signatures.length
but we don't want to add an implementation?While I'm here,
TypeChecker.signatureToSignatureDeclaration
has a non-nullable result. Then insignatureToMethodDeclaration
it's cast to another non-nullable type. Then it's tested for existence!There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, but I don't even understand what that check is for and what it's doing. @aozgaa?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See this comment (a few lines above): https://github.com/Microsoft/TypeScript/pull/19708/files#diff-6d6034083b71c2ed75493db0fe725ec5R88.
An example (haven't checked by actual debugging) might be
Then I think adding foo to
D
would be an example.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this check should be inside
createMethodImplementingSignatures
, which will just omit the body if we are in an ambient context.EDIT: no, we do not need an implementation signature in this case. Doh.