13

In Swift there is such concept as associated types.

protocol Container {
    associatedtype ItemType // Here it is.
    mutating func append(item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}

struct IntStack: Container {
    typealias ItemType = Int // Here again.
    mutating func append(item: Int) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Int {
        return items[i]
    }

    var items = [Int]()
    mutating func push(item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }
}

It's a kind of generic interfaces, but one important feature of associated types is that they can be refered from outside of the containing type.

var x: IntStack.ItemType = someIntStack.pop()

Is it possible to make somethig like this in TypeScript?

werediver
  • 4,667
  • 1
  • 29
  • 49

2 Answers2

9

There is no such feature for associated types in Typescript at the moment.

Alternatives to Associated Types

While there isn't anything in Typescript like this?...

// WILL NOT COMPILE
interface Add<T> {
  type Output;

  add(other: T): Output;
}

There are alternatives that can address some of the problems that associated types address to varying degrees.

Inferring the generic

You can infer the type of passed to a generic like so

type ArrayItem<T> = T extends Array<infer I> ? I : never;

And you can use this type like this.

const number: ArrayItem<Array<number>> = 1;

You can play around with it here on the Typescript Playground here.

Indexing a field's field

Say you had some types like these:

type StorageStage<T> = 
  | { kind: 'initial' }
  | { kind: 'loading', promise: Promise<Array<T>> }
  | { kind: 'loaded', storage: Array<T> };

class AsyncStorage<T> {
  state: StorageStage<T> = { kind: 'initial' };
}

You can use the index syntax to get the types of these fields if they're public.

const storageStage: AsyncStorage<number>["state"] = 
  { kind: 'loaded', storage: [1] }; 

Again, you check this out on the Typescript Playground here.

akst
  • 935
  • 11
  • 21
-1

According the documentation, it can be something like this:

abstract class Container<ItemType> {
    abstract append(item: ItemType): void;
    abstract count(): number;
    abstract subscript(i: number): ItemType;
}

class IntStack extends Container<number> {
    private items: Array<number> = [];

    append(item: number) {
        this.items.push(item);
    }
    count(): number {
        return this.items.length;
    }
    subscript(i: number): number {
        return this.items[i];
    }

    // Other functions
}

Update

In general, it is not possible to get generic type argument in runtime due to this "type" is used by typescript compiler for type checking only and does not compiles into any artifact in the result JavaScript code. You can check it in the Typescript Playground.

Inspite of it you can use some tricks, if you have a real value in runtime, as described in the Get type of generic parameter SO article.

Community
  • 1
  • 1
TSV
  • 7,538
  • 1
  • 29
  • 37
  • 2
    Wasn't his point though, that the type could be refered to from the outside? – Luka Jacobowitz Apr 03 '16 at 19:00
  • @LukaJacobowitz, exactly. I'll try to outline this point more clearly in the question. – werediver Apr 03 '16 at 19:08
  • 2
    @TSV, thank you for your effort, but the concept of generic types is pretty clear for me. – werediver Apr 03 '16 at 19:09
  • Thank you for clarification, guys. I've updated the answer. – TSV Apr 03 '16 at 19:52
  • This answer should probably be accepted, because it contains the words "it is not possible" (as of TypeScript v1.8). – werediver Apr 04 '16 at 08:52
  • Note that associated types do not in general imply being able to extract the type at runtime. Using this I have to write `fn>(c: C)` instead of the shorter `fn(c: C)`. All I'd want is the ability to access `C.ItemType` inside `fn` for typechecking, not for actual runtime behaviour. – WorldSEnder Dec 10 '18 at 20:49
  • 1
    @TSV I think you've misunderstood the question, associated data types have nothing to do with run time access of a generic type. It's more so an interface that asks implementers to define a paired type. For example, an interface for Addition where the return value of `a.add(b)` is specified by the implementor of the interface. Like `interface Add { type Return; add(other: T): Return; }` The answer is there are no associated data types in Typescript like there is in rust, Haskell, etc. – akst Mar 16 '20 at 23:49
  • @akst Thank you for the downvoting. If you read my answer you probably seen the "In general, it is not possible" phrase. And this is the answer. But at the same time I wanted to give a workaround for the topic starter, – TSV Mar 19 '20 at 18:33