Work on losing type restriction support

This is what I did in older versions of TypeScript, and I use this method in C #, but it does not work in the latest version of TypeScript.

Here's what worked in the past:

class Base<T extends Base<T>> { public children : Array<T>; doAction() { this.children[0].promise(); // used to work } promise() : T { return this; // used to work } } class Child extends Base<Child> { public myString: string; } new Child().promise().myString; // used to work 

Everything works in harmony.

Now in TypeScript 1.0, I get this error in the definition of Base<T extends Base<T>> :

A type parameter constraint cannot refer to any type parameter from the same list of type parameters.

How can I fix this example class to work without requiring any casting outside the classes or casting anything to "any"? Maybe this template should be changed?

+7
typescript
source share
3 answers

You are no longer allowed to use T here:

 class Base<T extends Base<T>> --------------------------^ 

You either need to make your class nonequivalent, or use:

 class Base<T extends Base<any>> 

The reason given was to make the compiler simpler:

In our ongoing efforts to simplify the language, we simplify what may have common limitations.

The added overhead in terms of type checking, error handling, and design complexity has not added enough extra expressiveness to make it useful for 1.0. We can revise this in future versions of TypeScript.

- Devastating Wiki Changes

+10
source share

I accepted Steve's answer because he suggested using Base<T extends Base<any>> , but I wanted to save a copy of the code change that fixed the problem in Stack Overflow:

 class Base<T extends Base<any>> { // 1. Set as any children: Array<T>; doAction() { this.children[0].promise(); } promise(): T { return <any> this; // 2. cast to any } } class Child extends Base<Child> { public myString: string; } new Child().promise().myString; 

This requires casting to anyone, but it's not so bad, since it is only in the base class. This change has no effect on the use of the Child or Base classes, so overall it was a very ideal alternative.


Update . In TS 1.7+, this can be done using polymorphic :

 class Base { children: Array<this>; doAction() { this.children[0].promise(); } promise(): this { return this; } } class Child extends Base { public myString: string; } new Child().promise().myString; 
+6
source share

promise() : T { return this; } promise() : T { return this; } this is a very weak assumption, which can be easily broken, because the demotion defined in the base class simply cannot work for any subclass

For example, if someone defines the class SuperChild extends Child {} , then promise() of SuperChild will give you Child instead of SuperChild .

Also, here is how you can make it work the way it is used to:

 class Base<T> { public children : Array<T>; constructor( private toSelf: () => T, private toBase: (something: T) => Base<T> ) { } doAction() { this.toBase(this.children[0]).promise(); // used to work } promise() : T { return this.toSelf(); // } } class Child extends Base<Child> { public myString: string; } function toThis() { return this; } new Child(toThis, x => x).promise().myString; // used to work 
0
source share

All Articles