M'sieur Toph'
M'sieur Toph'

Reputation: 2676

Typescript class method ONLY if generic type is string

Using a generic typed class, I would like some methods to be available ONLY IF generic type is 'string'.

With the following code

class MyClass<T = string> {
  value: T = null;

  setDate(m: Moment) {
    // could be used only if T is string
    value = m ? m.format('YYYY-MM-DD') : null;
  }

}

gives me the error

Argument of type 'string' is not assignable to parameter of type 'T'

which is really clear and totally intended : Moment.format() returns a string. :D

Is there a proper way to make the setDate() method avaible only if T is string ?

By advance, thank you very much.


I know I can fix the problem using m.format('YYYY-MM-DD') as unknown as T but it looks like a workaround, not a real typescript solution.

And I know about Conditional Types but I don't think it could fix my problem. At least, I did not find how to figure it out.

Upvotes: 2

Views: 675

Answers (2)

Jo Liss
Jo Liss

Reputation: 33104

To extend on Dimava's solution, you can set the type of this to never whenever the method should not exist:

class MyClass<T = string> {
  setDate(
    this: T extends string ? MyClass<T> : never,
    m: Moment
  ) {
    ...;
  }
}

The advantage of this strategy is that it works even for methods that don't take any arguments.

Note that this: Type is a special TypeScript syntax to type the this object. The this pseudo-parameter gets removed by TypeScript compiler, so the JavaScript engine never sees it.

Upvotes: 2

Dimava
Dimava

Reputation: 10899

make function have never arg on incorrect generic

type Moment = {format(s:string):string};

type SimpleEquals<T, V> = [T] extends [V] ? [V] extends [T] ? true : false : false;


class MyClass<T = string> {
  value: T | null = null;

    // could be used only if T is string
  setDate(m: SimpleEquals<T, string> extends true ? Moment : never) {
    this.value = m ? m.format('YYYY-MM-DD') as T : null;
  }
}

declare let m: Moment; 

new MyClass<string>().setDate(m) // ok

new MyClass<number>().setDate() //err
new MyClass<number>().setDate(m) //err

new MyClass<string | number>().setDate() //err
new MyClass<string | number>().setDate(m) //err

new MyClass<'qwe'>().setDate() //err
new MyClass<'qwe'>().setDate(m) //err

Upvotes: 3

Related Questions