Peeyush Kushwaha
Peeyush Kushwaha

Reputation: 3623

Type operator instead of declaring an interface that extends a class

According to typescript documentation, interfaces can extend classes in the following way:

class C {
  f1: string;
  f2: number;
}

interface I extends C {};

Note that I didn't extend I with extra members and that's why the body {} is empty.

The difference between I and C is of course that the plain object { f1: "hi", f2: 3} conforms to I but not to C (because while it has all the fields, it hasn't been produced from C's constructor)

I want to know, is there a type operator that can get I from C without having to declare I? e.g. something like operator<C> which is equivalent to I (you could perhaps even use type I = operator<C>?

Upvotes: 1

Views: 134

Answers (1)

mz8i
mz8i

Reputation: 812

Depending on your intended use, you might actually be fine just using C. For example:

class C {
  constructor(public f1: string, public f2: number){}
}

interface I extends C {}

function getF1(x: C) {
   return x.f1;
}

const x: C = { f1: "abc", f2: 123 };

getF1(x); // returns "abc"

Note when you use C in a type annotation, Typescript doesn't actually ensure that the object is really an instance of C, but rather that it has all the properties. What you wrote isn't really true, that the plain object doesn't "conform" to C. According to TS type system, it does.

If you had a more complex class and wanted for some reason to do something like get all the attributes but not the methods, you could use a TS mapped type with a conditional type that would return all the properties that are not a function.

In general, it's always worth using the TypeScript Playground to investigate the result of transpilation, to better realize the differences between classes and interfaces in TS.

const sth = { foo: 100 };
const i: I = { f1: "hi", f2: 3};
const fakeC: C = { f1: "hi", f2: 3};
const trueC = new C("h1", 3);

// does it conform to type C at transpilation time?
function ofTypeC(x: C) { return true; }
// ofTypeC(sth); // error
ofTypeC(i); // true
ofTypeC(fakeC); // true
ofTypeC(trueC); // true

// does it conform to type I at transpilation time?
function ofTypeI(x: I) { return true; }
// ofTypeI(sth); // error
ofTypeI(i); // true
ofTypeI(fakeC); // true
ofTypeI(trueC); // true

// is it an instance of class C at run time?
sth instanceof C // false
i instanceof C // false
fakeC instanceof C // false
trueC instanceof C // true

Or perhaps for sth more demonstrative of the caveats for using JS classes as TS types:

class Cat {
  public meow() {console.log("meow")}
}

class Dog {
  public meow() {console.log("Nah man")}
}

let cat: Cat = new Dog();
cat.meow();

Upvotes: 1

Related Questions