본문 바로가기
Web/JavaScript

[TypeScript] Interface

by llHoYall 2020. 10. 17.

TypeScript's type checking focuses on the shape that values have. This is called 'duck typing' or 'structural subtyping'.

In TypeScript, interfaces fill the role of naming these types and are a powerful way of defining contracts within code as well as contracts with code outside of the project.

interface IToDoItem {
  text: string;
  isCompleted: boolean
}

function addItem(item: IToDoItem) {
  console.log(item.text);
}

let newItem = { text: 'Write posting', isCompleted: false };
addItem(newItem);
// Writing posting

This is a simple example of an Interface.

Optional Properties

Some properties exist under certain conditions or may not be there at all.

interface IToDoItem {
  text: string;
  priority?: number;
  isCompleted: boolean;
}

function addItem(item: IToDoItem) {
  console.log(`${item.priority}: ${item.text}`);
}

let newItem1 = { text: 'Write posting', isCompleted: false };
addItem(newItem1);
// undefined: Write posting

let newItem2 = { text: 'Do a homework', priority: 1, isCompleted: false };
addItem(newItem2);
// 1: Do a homework

Each optional property denoted by a ? at the end of the property name in the declaration.

Readonly Properties

You can specify read-only properties by putting readonly before the name of the property.

interface IToDoItem {
  readonly owner: string;
  text: string;
  isCompleted: boolean;
}

function addItem(item: IToDoItem) {
  console.log(`${item.text} by ${item.owner}`);
  item.owner = 'kim'  // Error: it is a read-only property.
}

let newItem = { owner: 'hoya', text: 'Write posting', isCompleted: false };
addItem(newItem);
// Write posting by hoya

TypeScript provide a ReadonlyArray<T> type.

let roArr: ReadonlyArray<number> = [1, 2, 3];
let nArr: Array<number>;

roArr[0] = 4;  // Error: it only permits reading.
nArr = roArr;  // Error: readonly type cannot be assigned to the mutable type.
nArr = roArr as Array<number>;

If you want to assign a read-only type to the mutable type, you have to use a type assertion.

Remember it, variables use const whereas properties use readonly.

Function Types

To describe a function type with an interface, we give the interface a call signature.

interface CompareFunc {
  (param1: number, param2: number): boolean;
}
 
 let myFunc: CompareFunc = function (param1: number, param2: number) {
   return param1 > param2;
 };
 
 console.log(myFunc(2, 4));
 // false

The names of the parameters can be different.

interface CompareFunc {
  (param1: number, param2: number): boolean;
}
 
 let myFunc: CompareFunc = (p1: number, p2: number): boolean => {
   return p1 > p2;
 };
 
 console.log(myFunc(4, 2));
 // true

If you skip the specifying the argument types, TypeScript will infer it.

interface CompareFunc {
  (param1: number, param2: number): boolean;
}

let myFunc: CompareFunc = (p1, p2) => {
  return p1 > p2;
};

console.log(myFunc(1, 3));
// false

Indexable Types

Indexable types have an index signature that describes the types we can use to index into the object, along with the corresponding return types when indexing.

interface StringArray {
  [index: number]: string;
}

let arr: StringArray = ['tomato', 'potato'];

console.log(arr[1]);
// potato

There are two types of supported index signatures: string and number.

interface Fruit {
  name: string;
}

interface FruitArray {
  [x: number]: Fruit;
}

let arr: FruitArray = ['apple', 'banana', 'cherry'];  // Error: type string is not assignable to type Fruit

The type returned from a numeric indexer must be a subtype of the type returned from the string indexer.

This is because when indexing with a number, JavaScript will actually convert that to a string before indexing into an object.

Class Types

interface ShoppingCart {
  item: string;
  count: number;
  addItem(name: string): void;
  removeItem(name: string): void;
}

class Shop implements ShoppingCart {
  item: string;
  count: number;
  addItem(name: string) {
    ++this.count;
  }
  // Error: Property removeItem is missing.
  constructor() {
    this.item = "";
    this.count = 0;
  }
}

TypeScript is also possible, as in languages such as C# and Java, which explicitly enforcing that a class meets a particular contract.

Extending Interfaces

interface Fruit {
  name: string;
}

interface Mart extends Fruit {
  count: number;
}

let mart = { name: 'Apple', count: 5 } as Mart;
console.log(mart);
// {name: 'Apple', count: 5}

Interfaces can extend each other, and an interface can extend multiple interfaces.

Hybrid Types

interface Counter {
  (start: number): string;
  interval: number;
  reset(): void;
}

function getCounter(): Counter {
  const counter = function (start: number) {
    console.log(`This is a counter function.`)
  } as Counter;
  counter.interval = 30;
  counter.reset = function () {
    console.log('This is a reset function.')
  };
  return counter;
}

let c = getCounter();
c(10);
// This is a counter function.
c.reset();
// This is a reset function.
c.interval = 5;

Interfaces can describe the rich types present in JavaScript.

Interfaces Extending Classes

class Parent {
  private parentVar: any;
}

interface Child extends Parent {
  childFunc(): void;
}

class GrandChild implements Child {
  // Error: it is missing the 'parentVar' and 'childFunc'.
  private grandChildVar: any;
  grandChildFunc() {}
}

When an interface type extends a class type it inherits the members of the class but not their implementations.

It is as if the interface had declared all of the members of the class without providing an implementation.

댓글