microsoft / TypeScript

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

No auto-completion on typescript interface with classes implement #31956

Open ScreamZ opened 5 years ago

ScreamZ commented 5 years ago

Redirected by https://github.com/microsoft/vscode-cpptools/issues/3789

Type: LanguageService Describe the bug

To Reproduce

Create a file with the following :

import { Strategy } from "passport-local";

class SSOPlugin implements Plugin {

  constructor() {}

  init() {}
}

interface Plugin {
  authenticators?: Record<string, any>;

  strategies: { [key: string]: KuzzleStrategy };
}

interface KuzzleStrategy {
  config: {
    authenticator: string;
    fields: string[];
  };
  methods: {
    afterRegister?: () => any;
    create: () => any;
    delete: () => any;
    exists: () => any;
    getById?: () => any;
    getInfo?: () => any;
    update: () => any;
    validate: () => any;
    verify: () => any;
  };
}

export = SSOPlugin;

In constructor or any method using this. and auto-completion doesn't give anything.

But if I give the wrong types, the compiler complain (also red line on IDE).

Expected behavior

I should get auto-completion.

mjbvz commented 5 years ago

Do you see errors about SSOPlugin not correctly implementing the Plugin interface?

ScreamZ commented 5 years ago

When I write :

strategies = {
    dsfsf: {

    }
  } 

i have no completion in dsfsf object but strategies is yelling:

Property 'strategies' in type 'SSOPlugin' is not assignable to the same property in base type 'Plugin'.
  Type '{ dsfsf: {}; }' is not assignable to type '{ [key: string]: KuzzleStrategy; }'.
    Property 'dsfsf' is incompatible with index signature.
      Type '{}' is missing the following properties from type 'KuzzleStrategy': config, methodsts(2416)
Property 'strategies' has no initializer and is not definitely assigned in the constructor.ts(2564)

Totally differently, I can also see that in @types/react when you implement React.Component interface, the constructor is typed in interface but inference doesn't work in typescript

export default class Test implements React.Component<{a: string}, {a: string}> {
  constructor(props){
    super(props)
    // props is of type any, it should be ReadOnly<{a: string}>
  }
}

I think it's kind of related issue I'm describing.

Regards

mjbvz commented 5 years ago

Do you create a strategies property in your class? We only suggest implemented members, not things from the interface

ScreamZ commented 5 years ago

Edit : I used

strategies = {
    SSO: {
      config: { authenticator: "SSO", fields: ["username", "password"] },
      methods: {
        delete: "delete",
        exists: "exists",
        update: "update",
        validate: "validate",
        verify: "verify",
      },
      strategyOptions: {
        session: false,
      },
    },
  };

So yes, I'm using properties, what do you meant by things ?

mjbvz commented 5 years ago

Please share your complete code

ScreamZ commented 5 years ago
import to from "await-to-js";
import { Strategy } from "passport-local";
import SSO from "./sso";
import { SSOUser } from "./types";

class SSOPlugin implements Plugin {
  sso!: SSO;
  context: any;

  authenticators = {
    SSO: Strategy,
  };

  strategies = {
    SSO: {
      config: { authenticator: "SSO", fields: ["username", "password"] },
      methods: {
        delete: "delete",
        exists: "exists",
        update: "update",
        validate: "validate",
        verify: "verify",
      },
      strategyOptions: {
        session: false,
      },
    },
  };
  kuzzle: any; // TODO: find a way to bring types

  init(config: any, context: any) {
    this.sso = new SSO("https://mywebservice.com");
    this.context = context;
    this.kuzzle = context.accessors.sdk;
  }

  create() {
    return;
  }

  update() {
    return;
  }

  delete() {
    return;
  }

  exists() {
    return;
  }

  validate() {
    return;
  }

  async verify(_request: never, username: string, password: string) {
   // Some code   
  }
}

interface Plugin {
  authenticators?: Record<string, any>;

  strategies: { [key: string]: KuzzleStrategy };
}

interface KuzzleStrategy {
  config: {
    authenticator: string;
    fields: string[];
  };
  methods: {
    afterRegister?: string;
    create: string;
    delete: string;
    exists: string;
    getById?: string;
    getInfo?: string;
    update: string;
    validate: string;
    verify: string;
  };
}

export = SSOPlugin;

here methods is missing create: "create",, but you have no auto-complete to have this field. You can try it.

I have voluntarily for simplicity put the interface in the same file but in fact I would rather have it in another file, if you can achieve that I take it :)

My use case, I just want to bring typings on something that is not related to any specific imports. Kuzzle is as backend as a service that allows to build plugins, but plugins are simply JS class that are injected with some context functions that I would like to type.

thanks for help, Regards

mjbvz commented 5 years ago

And what is going wrong now? If I add a constructor like in your original question, I see the expected suggestions:

Screen Shot 2019-06-20 at 12 03 27 AM

Can you please just provide:

  1. The code.
  2. The exact thing I am trying to do in that code.
  3. What you expect to happen.
  4. What actually happens
ScreamZ commented 5 years ago

The code

import to from "await-to-js";
import { Strategy } from "passport-local";
import SSO from "./sso";
import { SSOUser } from "./types";

class SSOPlugin implements Plugin {
  sso!: SSO;
  context: any;

  authenticators = {
    SSO: Strategy,
  };

  strategies = {
    SSO: {
      config: { authenticator: "SSO", fields: ["username", "password"] },
      methods: {
        delete: "delete",
        // PUT CURSOR HERE AND PRESS AUTO-COMPLETE SHORCUT

        exists: "exists",
        update: "update",
        validate: "validate",
        verify: "verify",
      },
      strategyOptions: {
        session: false,
      },
    },
  };
  kuzzle: any; // TODO: find a way to bring types

  init(config: any, context: any) {
    this.sso = new SSO("https://mywebservice.com");
    this.context = context;
    this.kuzzle = context.accessors.sdk;
  }

  create() {
    return;
  }

  update() {
    return;
  }

  delete() {
    return;
  }

  exists() {
    return;
  }

  validate() {
    return;
  }

  async verify(_request: never, username: string, password: string) {
   // Some code   
  }
}

interface Plugin {
  authenticators?: Record<string, any>;

  strategies: { [key: string]: KuzzleStrategy };
}

interface KuzzleStrategy {
  config: {
    authenticator: string;
    fields: string[];
  };
  methods: {
    afterRegister?: string;
    create: string;
    delete: string;
    exists: string;
    getById?: string;
    getInfo?: string;
    update: string;
    validate: string;
    verify: string;
  };
}

export = SSOPlugin;

What I'm trying to do

I want to add auto-completion to a class properties using an interface, by defining the interface inside the class file or outside the class file. In the above example it's inside.

What I currently have

Screenshot 2019-06-20 at 13 50 26

Interface define that the create method is required, I should get auto-completion for this property, as you can see the auto-completion is not suggesting create.

What I should get

As the only missing property is create VSCODE should suggest me to get this property using auto-completion.


Guys from vscode told be this is the issue where the question should exists, I already tried in the vscode repo.

mjbvz commented 5 years ago

Thanks.

Minimal example:

class Foo implements IFoo {
    methods = {
        x: '',
        |
    }
}

interface IFoo {
    methods: {
        x: string;
        y: string;
    };
}
  1. Trigger suggestions at |
  2. No suggestion for y shown
ScreamZ commented 5 years ago

@mjbvz That's it ! Good job

Do you take this in charge ? Or should I migrate this to a vscode repository (Is it the good place for that issue ?)

mjbvz commented 5 years ago

It is in the correct repo

ScreamZ commented 4 years ago

Hello all, any news on that? I'm experiencing it using NestJS CustomScalar interface… Get complicated 😅

@RyanCavanaugh @mjbvz Maybe you have some ideas, not exactly the same issue as reported above

export class PasswordScalar implements CustomScalar<string, ObjectId> {
  parseLiteral(ast) {
    // ast is considered "any"
  }
}

// Those are taken from NestJS, the most advanced typescript framework for nodejs
export interface CustomScalar<T, K> {
    description?: string;
    parseValue: GraphQLScalarValueParser<K>;
    serialize: GraphQLScalarSerializer<T>;
    parseLiteral: GraphQLScalarLiteralParser<K>;
}

export type GraphQLScalarLiteralParser<TInternal> = (
  valueNode: ValueNode,
  variables: Maybe<{ [key: string]: any }>,
) => Maybe<TInternal>;

For not having ast considered as any, is have to parseLiteral(ast: ValueNode) {

adamsuskin commented 5 months ago

I believe this is the same issue, but including a screenshot here of what I'm trying to do. The cursor selection and the quick fix box is where the suggestion is expected to appear for namedField but isn't.

Screenshot 2024-04-07 at 10 14 06 PM
adamsuskin commented 5 months ago

@RyanCavanaugh @mjbvz if we think this is beginner-friendly, I'd be happy to work on contributing it