Interface Design and Type Contracts
1. Interface Declaration and Extension
Feature
Syntax
Description
Use Case
Basic Interface
interface Name { }
Define object shape with properties and methods
Object contracts, API shapes
Interface Extension
interface B extends A
Inherit properties from parent interface
Type hierarchies, specialization
Multiple Extension
extends A, B, C
Extend multiple interfaces simultaneously
Mixin patterns, composition
Hybrid Type
Callable + properties
Object that is both callable and has properties
Function objects, jQuery-like APIs
Example: Basic interface declaration
// Basic interface
interface User {
id : number ;
name : string ;
email : string ;
}
const user : User = {
id: 1 ,
name: "Alice" ,
email: "alice@email.com"
};
// Interface with methods
interface Calculator {
add ( a : number , b : number ) : number ;
subtract ( a : number , b : number ) : number ;
}
const calc : Calculator = {
add : ( a , b ) => a + b,
subtract : ( a , b ) => a - b
};
// Nested interfaces
interface Address {
street : string ;
city : string ;
country : string ;
}
interface Person {
name : string ;
address : Address ;
}
Example: Interface extension
// Single extension
interface Animal {
name : string ;
age : number ;
}
interface Dog extends Animal {
breed : string ;
bark () : void ;
}
const dog : Dog = {
name: "Buddy" ,
age: 3 ,
breed: "Golden Retriever" ,
bark () { console. log ( "Woof!" ); }
};
// Multiple extension
interface Timestamped {
createdAt : Date ;
updatedAt : Date ;
}
interface Versioned {
version : number ;
}
interface Document extends Timestamped , Versioned {
id : string ;
content : string ;
}
// Override inherited properties
interface Shape {
color : string ;
}
interface ColoredSquare extends Shape {
color : "red" | "blue" | "green" ; // Narrowed type
size : number ;
}
// Extension chain
interface Entity {
id : string ;
}
interface NamedEntity extends Entity {
name : string ;
}
interface User extends NamedEntity {
email : string ;
}
2. Optional Properties and Readonly Modifiers
Modifier
Syntax
Description
Use Case
Optional Property
prop?: Type
Property may be undefined or missing
Partial data, optional fields
Readonly Property
readonly prop: Type
Property cannot be reassigned after initialization
Immutable data, constants
Readonly Optional
readonly prop?: Type
Combine both modifiers - optional and immutable
Optional config values
Example: Optional properties
// Optional properties
interface UserProfile {
id : number ;
name : string ;
email : string ;
phone ?: string ; // Optional
avatar ?: string ; // Optional
bio ?: string ; // Optional
}
// Valid with or without optional properties
const user1 : UserProfile = {
id: 1 ,
name: "Alice" ,
email: "alice@email.com"
};
const user2 : UserProfile = {
id: 2 ,
name: "Bob" ,
email: "bob@email.com" ,
phone: "555-1234" ,
avatar: "avatar.jpg"
};
// Function accepting optional properties
function createProfile ( data : UserProfile ) : void {
console. log (data.name);
// Check optional properties before use
if (data.phone) {
console. log ( `Phone: ${ data . phone }` );
}
}
Example: Readonly properties
// Readonly properties
interface Config {
readonly apiUrl : string ;
readonly apiKey : string ;
readonly timeout : number ;
retries : number ; // Mutable
}
const config : Config = {
apiUrl: "https://api.example.com" ,
apiKey: "secret-key" ,
timeout: 5000 ,
retries: 3
};
// config.apiUrl = "https://new-url.com"; // Error: readonly
// config.timeout = 10000; // Error: readonly
config.retries = 5 ; // OK - mutable
// Readonly arrays and objects
interface Data {
readonly tags : readonly string [];
readonly metadata : Readonly <{ key : string ; value : string }>;
}
const data : Data = {
tags: [ "typescript" , "tutorial" ],
metadata: { key: "author" , value: "Alice" }
};
// data.tags.push("new"); // Error: readonly array
// data.metadata.key = "new"; // Error: readonly object
// Readonly with optional
interface Settings {
readonly id : string ;
readonly theme ?: "light" | "dark" ;
readonly fontSize ?: number ;
}
3. Function Types and Method Signatures in Interfaces
Pattern
Syntax
Description
Example
Method Signature
method(args): Type
Method within interface - regular syntax
getName(): string
Function Property
prop: (args) => Type
Property that is a function - arrow syntax
callback: () => void
Call Signature
(args): Type
Make interface itself callable
(x: number): string
Construct Signature
new (args): Type
Constructor function signature
new (name: string): User
Overloaded Methods
Multiple signatures
Method with multiple call signatures
Different parameter combinations
Example: Method signatures
// Method signatures
interface Repository < T > {
find ( id : string ) : T | undefined ;
findAll () : T [];
save ( item : T ) : void ;
delete ( id : string ) : boolean ;
count () : number ;
}
// Function properties vs method signatures
interface EventEmitter {
// Method signature (preferred)
on ( event : string , handler : Function ) : void ;
off ( event : string , handler : Function ) : void ;
// Function property (allows reassignment)
emit : ( event : string , ... args : any []) => void ;
}
// Optional methods
interface Logger {
log ( message : string ) : void ;
error ( message : string ) : void ;
warn ? ( message : string ) : void ; // Optional method
debug ? ( message : string ) : void ; // Optional method
}
// Generic method signatures
interface Transformer {
transform < T , U >( input : T ) : U ;
validate < T >( data : T ) : boolean ;
}
Example: Call and construct signatures
// Call signature - callable interface
interface StringFormatter {
( input : string ) : string ; // Call signature
prefix : string ; // Also has properties
suffix : string ;
}
function createFormatter ( pre : string , suf : string ) : StringFormatter {
const formatter = (( input : string ) => {
return `${ formatter . prefix }${ input }${ formatter . suffix }` ;
}) as StringFormatter ;
formatter.prefix = pre;
formatter.suffix = suf;
return formatter;
}
const format = createFormatter ( "[" , "]" );
console. log ( format ( "test" )); // "[test]"
// Construct signature - constructor interface
interface UserConstructor {
new ( name : string , age : number ) : User ;
}
class User {
constructor ( public name : string , public age : number ) {}
}
function createUser ( ctor : UserConstructor , name : string , age : number ) : User {
return new ctor (name, age);
}
const user = createUser (User, "Alice" , 30 );
// Hybrid type - callable with properties
interface Counter {
( start : number ) : string ;
interval : number ;
reset () : void ;
}
Example: Method overloading in interfaces
// Overloaded method signatures
interface Formatter {
format ( value : string ) : string ;
format ( value : number ) : string ;
format ( value : boolean ) : string ;
format ( value : Date ) : string ;
}
const formatter : Formatter = {
format ( value : string | number | boolean | Date ) : string {
if ( typeof value === "string" ) return value;
if ( typeof value === "number" ) return value. toString ();
if ( typeof value === "boolean" ) return value ? "true" : "false" ;
return value. toISOString ();
}
};
// Generic overloads
interface Parser {
parse ( input : string ) : any ;
parse < T >( input : string , type : new () => T ) : T ;
}
// Rest parameters in interface
interface Logger {
log ( message : string , ... args : any []) : void ;
}
4. Index Signatures for Dynamic Properties
Type
Syntax
Description
Use Case
String Index
[key: string]: Type
Any string key maps to specified type
Dictionaries, dynamic objects
Number Index
[index: number]: Type
Numeric indices for array-like structures
Array-like objects, tuples
Symbol Index TS 4.4+
[key: symbol]: Type
Symbol keys for unique properties
Well-known symbols, metadata
Template Literal Index
[K: `prefix${string}`]
Pattern-based property keys
Prefixed properties, conventions
Example: String index signatures
// Basic string index signature
interface Dictionary < T > {
[ key : string ] : T ;
}
const scores : Dictionary < number > = {
"Alice" : 95 ,
"Bob" : 87 ,
"Charlie" : 92
};
// Mixed: known properties + index signature
interface UserData {
id : number ;
name : string ;
[ key : string ] : any ; // Additional dynamic properties
}
const userData : UserData = {
id: 1 ,
name: "Alice" ,
age: 30 , // OK - additional property
email: "alice@email.com" // OK
};
// Readonly index signature
interface ReadonlyDict {
readonly [ key : string ] : string ;
}
const dict : ReadonlyDict = { a: "hello" };
// dict.a = "world"; // Error: readonly
// dict.b = "new"; // Error: readonly
// Constrained index signature
interface NumberDict {
[ key : string ] : number ;
length : number ; // OK - number type
// name: string; // Error: must be number
}
Example: Number and advanced index signatures
// Number index signature
interface StringArray {
[ index : number ] : string ;
}
const arr : StringArray = [ "a" , "b" , "c" ];
const first : string = arr[ 0 ];
// Both string and number index
interface MixedArray {
[ index : number ] : string ;
[ key : string ] : string | number ;
length : number ;
}
// Template literal index signature (TS 4.4+)
interface DataAttributes {
[ key : `data-${ string }` ] : string ;
}
const attrs : DataAttributes = {
"data-id" : "123" ,
"data-name" : "test" ,
// "id": "456" // Error: must match pattern
};
// Generic index signature
interface Cache < T > {
[ key : string ] : T | undefined ;
get ( key : string ) : T | undefined ;
set ( key : string , value : T ) : void ;
}
// Symbol index signature (TS 4.4+)
interface Symbolized {
[ key : symbol ] : any ;
}
const sym = Symbol ( "id" );
const obj : Symbolized = {
[sym]: 123
};
5. Interface Merging and Declaration Merging
Concept
Description
Use Case
Declaration Merging
Multiple interface declarations with same name merge into one
Augmenting libraries, modules
Module Augmentation
Add types to existing modules or libraries
Extending third-party types
Global Augmentation
Add properties to global namespace
Window, process extensions
Namespace Merging
Interfaces can merge with namespaces/functions
Hybrid declarations
Example: Declaration merging
// Basic interface merging
interface User {
id : number ;
name : string ;
}
interface User {
email : string ;
age : number ;
}
// Merged result:
// interface User {
// id: number;
// name: string;
// email: string;
// age: number;
// }
const user : User = {
id: 1 ,
name: "Alice" ,
email: "alice@email.com" ,
age: 30
};
// Method overloading via merging
interface Calculator {
add ( a : number , b : number ) : number ;
}
interface Calculator {
add ( a : string , b : string ) : string ;
}
// Both overloads available
const calc : Calculator = {
add ( a : any , b : any ) : any {
return a + b;
}
};
Example: Module and global augmentation
// Augment existing module
declare module "express" {
interface Request {
user ?: {
id : string ;
name : string ;
};
}
}
// Global augmentation
declare global {
interface Window {
customProperty : string ;
myAPI : {
version : string ;
init () : void ;
};
}
interface Array < T > {
customMethod () : T [];
}
}
// Now available globally
window.customProperty = "value" ;
window.myAPI. init ();
// Augment NodeJS global
declare global {
namespace NodeJS {
interface ProcessEnv {
CUSTOM_VAR : string ;
API_KEY : string ;
}
}
}
// Type-safe environment variables
const apiKey = process.env. API_KEY ; // string
Note: Interface merging only works with interfaces , not type
aliases. This is one key difference between interfaces and types. Use merging for library augmentation and
extending third-party types.
6. Implementing Interfaces in Classes
Feature
Syntax
Description
Use Case
Single Implementation
class X implements I
Class must satisfy interface contract
Type contracts, polymorphism
Multiple Implementation
implements I1, I2, I3
Class implements multiple interfaces
Multiple capabilities, composition
Interface + Extension
extends A implements I
Combine inheritance and interface implementation
OOP patterns, frameworks
Generic Implementation
class X<T> implements I<T>
Implement generic interface with type parameter
Generic containers, adapters
Example: Basic interface implementation
// Define interface
interface Printable {
print () : void ;
}
interface Serializable {
serialize () : string ;
deserialize ( data : string ) : void ;
}
// Implement single interface
class Document implements Printable {
constructor ( private content : string ) {}
print () : void {
console. log ( this .content);
}
}
// Implement multiple interfaces
class DataModel implements Printable , Serializable {
constructor ( private data : any ) {}
print () : void {
console. log ( JSON . stringify ( this .data, null , 2 ));
}
serialize () : string {
return JSON . stringify ( this .data);
}
deserialize ( data : string ) : void {
this .data = JSON . parse (data);
}
}
// Interface ensures implementation
// class InvalidDoc implements Printable {
// // Error: missing print() method
// }
Example: Generic interface implementation
// Generic interface
interface Repository < T > {
find ( id : string ) : T | undefined ;
findAll () : T [];
save ( item : T ) : void ;
delete ( id : string ) : boolean ;
}
// Implement with specific type
class UserRepository implements Repository < User > {
private users : Map < string , User > = new Map ();
find ( id : string ) : User | undefined {
return this .users. get (id);
}
findAll () : User [] {
return Array. from ( this .users. values ());
}
save ( user : User ) : void {
this .users. set (user.id. toString (), user);
}
delete ( id : string ) : boolean {
return this .users. delete (id);
}
}
// Generic class implementing generic interface
class GenericRepository < T extends { id : string }> implements Repository < T > {
private items = new Map < string , T >();
find ( id : string ) : T | undefined {
return this .items. get (id);
}
findAll () : T [] {
return Array. from ( this .items. values ());
}
save ( item : T ) : void {
this .items. set (item.id, item);
}
delete ( id : string ) : boolean {
return this .items. delete (id);
}
}
Example: Combining inheritance and interfaces
// Base class
class Entity {
constructor ( public id : string , public createdAt : Date ) {}
}
// Interfaces
interface Timestamped {
updatedAt : Date ;
touch () : void ;
}
interface Versioned {
version : number ;
incrementVersion () : void ;
}
// Extend class and implement interfaces
class Document extends Entity implements Timestamped , Versioned {
updatedAt : Date ;
version : number ;
constructor (
id : string ,
public content : string
) {
super (id, new Date ());
this .updatedAt = new Date ();
this .version = 1 ;
}
touch () : void {
this .updatedAt = new Date ();
}
incrementVersion () : void {
this .version ++ ;
this . touch ();
}
}
// Polymorphism with interfaces
function printTimestamp ( obj : Timestamped ) : void {
console. log ( `Updated: ${ obj . updatedAt }` );
}
const doc = new Document ( "1" , "Hello" );
printTimestamp (doc); // OK - Document implements Timestamped
Interface Design Best Practices
Use interfaces for object shapes and public contracts
Prefer readonly for immutable properties
Use optional properties for partial data
Apply index signatures for flexible dictionaries
Leverage declaration merging for library augmentation
Implement interfaces in classes for polymorphism
Use method signatures over function properties for better type checking