Skip to content

Suggestion: Abstract/Opaque types #15408

Closed
@jonaskello

Description

@jonaskello

When using a functional programming style it is common to have a module that contains a record type, constructor functions for that record type and other function that operate on that type. For example consider a simple module for a date record:

date.ts

export interface Date { 
    readonly day : number,
    readonly month : number,
    readonly year : number
};

export function createDate(day: number, month: number, year: number): Date {
    return {day, month, year};
}

export function diffDays(date1: Date, date2: Date): number {
 // Imagine implementation that calculates 
 // the difference in days between date1 and date2
}

// Extract the year from the date
export function year(date: Date): number {
    return date.year;
}

This works well to start with. However we may at some point want to refactor the structure of the Date type. Let's say we decide it is better to have it store ticks since epoch:

export interface Date { 
    readonly ticks : number,
};

The problem is now that other modules may have directly read the Date.years property instead of going through our year() function. So other modules are now directly coupled to the type's structure, making our refactoring hard because we need to go through and change all calling modules instead of just changing our date module. (In real-world scenarios the record type is more complex and perhaps nested).

The Ocaml programming language has a nice solution for this called abstract types (see docs here, under the heading "Abstract Types".

The idea is that we export the fact that there is a Date type and you have to use that type to call our functions. However we do not export the structure of the Date type. The structure is only known within our date.ts module.

A suggestion for the syntax could be:

date.ts

export abstract interface Date { 
    readonly day : number,
    readonly month : number,
    readonly year : number
};

other.ts

Import { Date, createDate, year } from "./date"

const date = createDate(2017, 1, 1);
const a = year(date); // OK
const b = date.year; // ERROR

Maybe typescript already has a construct for achieving something similar but my research have not found any.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions