microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
101.07k stars 12.49k forks source link

Code fix spelling suggestions for class fields based on lexical scope #45908

Open acutmore opened 3 years ago

acutmore commented 3 years ago

Suggestion

Issue raised to separately track discussion started here: https://github.com/microsoft/TypeScript/pull/44648/files#r709632217 as suggested by @sandersn

Right now code fix spelling suggestions for property names are based on the receiver type. When the receiver type is wide, perhaps unknown, further correct candidate names could be collected from the fields in any surrounding classes. This could potentially only apply to private fields, where they can only be used within the class, or extended to all fields.

πŸ” Search Terms

spelling, suggestion, code fix, class fields, private fields

βœ… Viability Checklist

My suggestion meets these guidelines:

⭐ Suggestion

πŸ“ƒ Motivating Example

class MyClass {
  #brand;

  static isMyClass(v: object): v is MyClass {
    return #brad in v; // provide code-fix here: change 'brad' to 'brand'
  }
}

Private field lookup, unlike traditional property look up, is scoped to within the class. This provides a finite, and lexically discoverable, set of candidate field names.

πŸ’» Use Cases

The reciever type can be too wide to use for candidate names in these cases:

❔ Questions

from here

acutmore commented 3 years ago

This was originally implemented as part of https://github.com/microsoft/TypeScript/pull/44648/files but removed to keep that PR focused, and allow this to be discussed separately.

function getSuggestedSymbolForNonexistentProperty(propertyName: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined {
    let props = getPropertiesOfType(containingType);
    let name: string;
    if (typeof propertyName === "string") {
        name = propertyName;
    }
    else {
        name = idText(propertyName);
        const parent = propertyName.parent;
        if (isPropertyAccessExpression(parent)) {
            props = filter(props, prop => isValidPropertyAccessForCompletions(parent, containingType, prop));
        }
    }
    const suggestion = getSpellingSuggestionForName(name, props, SymbolFlags.Value);
    if (suggestion) {
        return suggestion;
    }

    // New logic:
    if (typeof propertyName !== "string" && isPrivateIdentifier(propertyName)) {
        const privateIdentifiers: Symbol[] = [];
        forEachEnclosingClass(propertyName, (klass: ClassLikeDeclaration) => {
            forEach(klass.members, member => {
                if (isPrivateIdentifierClassElementDeclaration(member)) {
                    privateIdentifiers.push(member.symbol);
                }
            });
        });
        return getSpellingSuggestionForName(name, privateIdentifiers, SymbolFlags.Value);
    }
    return undefined;
}