Typescript: Poor deprecated messages - types should almost never be displayed.

Created on 27 Oct 2020  路  17Comments  路  Source: microsoft/TypeScript

/** @deprecated - this is bad advice */
declare function crossTheStreams(): void

crossTheStreams()

Expected: 'crossTheStreams' is deprecated.
Actual: '(): void' is deprecated

Notice the type in place of the entity name, notice the lack of a period at the end.

image

Bug Error Messages Moderate Fix Available help wanted

Most helpful comment

I see I had the wrong idea of what valueDeclaration was. It's conditionally set.
image

All 17 comments

Note: This should normally happen only with overloads. It's not great with overloads either, so probably the overload message should be '{0}' with type '{1}' is deprecated. instead of putting a type into '{0}' is deprecated

I assume this isn't as simple as using signature.declaration.name.escapedText?

Like, signature.declaration could be JSDocSignature.
How would you get the function name from a JSDocSignature?

This should normally happen only with overloads.

Only if not every overload is deprecated. If all overloads are deprecated, showing the type doesn't help.

I assume this isn't as simple as using signature.declaration.name.escapedText?

It is better to use one of the various helpers for printing out the expression when a name is available. We've done this in a couple of places where we try to special case this when the invoked expression is an identifier/property access.

tryGetPropertyAccessOrIdentifierToString is one

getCalledExpressionName is also similar, but is weird in that it'll give a partial result if the expression gets a bit too complex (e.g. foo().bar.baz will give you bar.baz).

Probably what you want is just to do something even more basic where you try grabbing the name.text off a property access, the text off an Identifier, and stop there. Not sure of any other helper functions that do this, but I'm sure this isn't the only place.

I see getNameOfDeclaration, too

The declaration name isn't always useful because it might be anonymous (it might just be a call signature, it might come from a default import). The most relevant name for this error message is the one that is being used locally.

Something useful might be to provide a related span linking back to where the deprecation comes from, so that would link users back to the relevant declaration.

The declaration was marked as deprecated here.

I have no clue what I'm doing but I tried something like this (I should remove the "lol"),

        function checkDeprecatedSignature(signature: Signature, node: CallLikeExpression) {
            if (signature.declaration && signature.declaration.flags & NodeFlags.Deprecated) {
                const suggestionNode = getDeprecatedSuggestionNode(node);
                const deprecation = getJSDocDeprecatedTag(signature.declaration);
                if (deprecation == undefined) {
                    errorOrSuggestion(/*isError*/ false, suggestionNode, Diagnostics._0_is_deprecated, signatureToString(signature));
                } else {
                    const diagnostic = createDiagnosticForNode(
                        suggestionNode, 
                        Diagnostics._0_is_deprecated, 
                        signatureToString(signature) + "lol"
                    );
                    addRelatedInfo(
                        diagnostic,
                        createDiagnosticForNode(deprecation, Diagnostics.The_implementation_signature_is_declared_here)
                    );
                    suggestionDiagnostics.add({ ...diagnostic, category: DiagnosticCategory.Suggestion });
                }
            }
        }

And got something like this,
image

(I know it's the wrong diagnostic lol)


I'm surprised getJSDocDeprecatedTag() works for this,
image

(I should remove the "lol"),

But it's so fun!

Yeah, I don't know what the code currently looks like, but on the node, you should be able to getInvokedExpression and get the text using any of the methods I described above.

I noticed this doesn't produce the deprecation notice on foo(blah)

image

getCalledExpressionName seems to be in src/services/navigationBar.ts and not exported.


Huzzah!
image


Regarding overloads, this is pretty good. (ignore the eslint red squigglies)

image

I think I've got it down,
image

@DanielRosenwasser
Is it normal for Symbol.declarations to be a non-empty array but for valueDeclaration to be undefined?

I assumed valueDeclaration was just a shorthand for declarations[0].

This happens in tests/cases/fourslash/jsdocDeprecated_suggestion1.ts

Smaller repro,

//a.ts
/** @deprecated */
export function bar() {

}

/** @deprecated */
export type QW = 3;
//b.ts
import { bar, QW } from './a';

The import of bar is fine. It has valueDeclaration set.

However, QW has declarations.length == 1 but valueDeclaration == undefined.


image

It also seems like tryGetPropertyAccessOrIdentifierToString() doesn't like namespaces + optional chains.

In tests/cases/fourslash/jsdocDeprecated_suggestion1.ts,
the line containing foo?.[[|"faff"|]]?.() trips up tryGetPropertyAccessOrIdentifierToString().

Minimal repro,

export namespace foo {
    /** @deprecated */
    export function faff() { }
}

foo?.["faff"]?.();

image
image


If you replace the namespace with a variable, it works.
image


Adding this seems to fix it,

        else if (isElementAccessExpression(expr)) {
            const baseStr = tryGetPropertyAccessOrIdentifierToString(expr.expression);
            if (baseStr !== undefined && isPropertyName(expr.argumentExpression)) {
                return baseStr + "." + getPropertyNameForPropertyNameNode(expr.argumentExpression);
            }
        }

image


Unfortunately... It fails in this case,
image

Is it normal for Symbol.declarations to be a non-empty array but for valueDeclaration to be undefined?
I assumed valueDeclaration was just a shorthand for declarations[0].

Basically, As the name of valueDeclaration, Only existed when some symbol could be an value

It trips me up, though, because I see it declared as type Declaration, not Declaration|undefined.
image

It also doesn't make sense for a symbol to exist without a declaration, in my head.

First value declaration means first declaration of value

You can also find all declarations(includes valueDeclaration) in the declarations field

I see I had the wrong idea of what valueDeclaration was. It's conditionally set.
image

Was this page helpful?
0 / 5 - 0 ratings