Struct Embedding in TypeScript

// TypeScript supports composition through object spreading and
// interfaces, which can be used to achieve similar functionality
// to Go's struct embedding.

interface Base {
    num: number;
    describe(): string;
}

class BaseImpl implements Base {
    constructor(public num: number) {}

    describe(): string {
        return `base with num=${this.num}`;
    }
}

// A `Container` composes a `Base`. We use object spreading
// to include all properties of Base.
interface Container extends Base {
    str: string;
}

function main() {
    // When creating objects, we use object spreading to
    // include all properties of the base object.
    const co: Container = {
        ...new BaseImpl(1),
        str: "some name"
    };

    // We can access the base's fields directly on `co`.
    console.log(`co={num: ${co.num}, str: ${co.str}}`);

    // We can also access the base's methods directly on `co`.
    console.log("describe:", co.describe());

    // TypeScript uses structural typing, so any object that has
    // a `describe` method of the correct signature is considered
    // to implement the `Describer` interface.
    interface Describer {
        describe(): string;
    }

    // `co` is automatically considered to implement `Describer`
    // because it has a `describe` method.
    const d: Describer = co;
    console.log("describer:", d.describe());
}

main();

This TypeScript code demonstrates concepts similar to struct embedding in Go. Here’s an explanation of the key points:

  1. We define a Base interface and a BaseImpl class that implements it. This is similar to the base struct in the Go example.

  2. We create a Container interface that extends Base and adds an additional str property. This is similar to the container struct in Go.

  3. When creating an object of type Container, we use object spreading (...new BaseImpl(1)) to include all properties of the base object. This achieves a similar effect to Go’s struct embedding.

  4. We can access the base’s fields and methods directly on the Container object, just like in the Go example.

  5. TypeScript uses structural typing, which means that any object with a describe method of the correct signature is considered to implement the Describer interface. This is similar to how the container in Go automatically implemented the describer interface.

To run this TypeScript code, you would typically use the TypeScript compiler (tsc) to compile it to JavaScript, and then run the resulting JavaScript with Node.js. However, you can also use ts-node to run TypeScript directly:

$ npm install -g ts-node typescript
$ ts-node struct-embedding.ts
co={num: 1, str: some name}
describe: base with num=1
describer: base with num=1

This example demonstrates how TypeScript can achieve similar functionality to Go’s struct embedding through its type system and object composition features.