TypeScript Day 5 — Object Orientation (Part 2)
3. Interfaces
An interface works much like an abstract class; the difference is that every method and property in an interface has no real value — in other words, everything in an interface is abstract. An interface is mainly responsible for defining the shape of a class: its properties and its methods. An interface can also constrain the shape of an object, and an object only matches the interface when it carries all the properties and methods the interface declares. At the same time, a class can implement an interface, and when it does so the class has to provide all the properties declared by the interface.
Example (checking an object's type):
interface Person{ name: string; sayHello():void; } function fn(per: Person){ per.sayHello(); } fn({ name:'Sun Wukong', sayHello() { console.log(`Hello, I am ${this.name}`) } });
Example (implementing it)
interface Person{ name: string; sayHello():void; } class Student implements Person{ constructor(public name: string) { } sayHello() { console.log('Hello everyone, I am '+this.name); } }
Note:
interfacecan be written more than once, and when a class implements the interface the compiler merges the structure of the same-named interfaces
4. Generics (Generic)
When you define a function or a class, there are situations where the concrete type involved cannot be pinned down (the types of the return value, the parameters or a property are unknown), and that is where generics earn their keep.
An example:
function test(arg: any): any{ return arg; }In the example above,
testtakes a parameter whose type is unknown, but you can be sure the return type is the same as the parameter's type. Since the type is unknown, both the parameter and the return value are typedany— and that is clearly not right: first,anyswitches off TS's type checking, and second, it does not express that the parameter and the return value share a typeUsing a generic:
function test<T>(arg: T): T{ return arg; }The
<T>here is the generic, andTis the name we give that type (it does not have to beT). Once it is declared you can useTinside the function to stand for that type. So generics are easy to grasp: they stand for some type.So how do we use the function above?
Way one (use it directly):
test(10)You can pass the argument straight in and TS infers the type, but sometimes the compiler cannot infer it, and then you need the form below
Way two (specify the type):
test<number>(10)You can also pin the generic by hand after the function name
You can declare several generics at once, separated by commas:
function test<T, K>(a: T, b: K): K{ return b; } test<number, string>(10, "hello");When you use generics you can treat a generic exactly like an ordinary class
Classes can take generics too:
class MyClass<T>{ prop: T; constructor(prop: T){ this.prop = prop; } }
You can also constrain the range a generic accepts
interface MyInter{ length: number; } function test<T extends MyInter>(arg: T): number{ return arg.length; }T extends MyIntersays the genericTmust be a subtype ofMyInter; it does not have to be an interface type — classes work the same way.
Important
This article was translated by Hermes Agent; the original text can be found at here. The translation is for reference only. Translation time: 2026-11-05
