Translate

Thursday 5 April 2018

Generic type constraints in Typescript

Platform: nodejs
npm install typescript, ts-node, mocha, babel

Generics from the C# days of 2005 onwards is a much-underused technique to implement type safety and security.

Here is a look at the fascinating feature using a mocha test and a couple of es6 classes and interface. Needless to say, this post, too, is inspired by a question from SO.

First, the premise.

An interface - interface DiscreteLinearOrder - with a generic type constraint - - declares two methods - next and lessThanOne - which are implemented in all deriving classes.

Another class that implements this interface imposes its own constraint in this form -

class DLOinterval<V, U extends DiscreteLinearOrder<any>>
implements DiscreteLinearOrder -

for the simple requirement that any consuming object is not typed as

const aaa = new abc.DLOinterval<number, string
>

or any other type but DiscreteLinearOrder interface type.

Like so,

const aaa = new abc.DLOinterval<number, abc.DLOnumber>

where abc.DLOnumber is another class implementing the DiscreteLinearOrder interface.




The interesting aspect that I originally chose this scenario was to explore the type inference happening at compile time in generics ie.,



The red squiggly line, in the above screenshot, indicates an error that since T is already the first parameter, typing another parameter with the same constraint causes the compiler to build an inference tree from the same parameter list, which had it not been a generic type would have simply been explained as a variable name already used but since T is not a variable but a type, it demonstrates that its duplicate usage causes a constraint to become almost like an infinite constraint as they both refer to each other!

Below is the code to be compiled with 

  1. tsc   
  2. mocha --compilers <path to ts-node\register> testfilename.ts
// testname.ts
import {expect} from 'chai'
import * as abc from './discretelo'
import 'mocha'
describe('a test', () => { 
it('interface', function(){
const aaa = new abc.DLOinterval<number, abc.DLOnumber>
(new abc.DLOnumber(3),new abc.DLOnumber(5),null,2)
    expect(aaa.next()).to.equal(3)
})
});
// discretelo.ts

interface DiscreteLinearOrder<T> {
    next:()=>T;
    lessThan(y:T):Boolean;
}
export class DLOnumber implements DiscreteLinearOrder<number> {
    private value: number;
    constructor(x: number) { this.value = x; }
    next = () => { return(this.value + 1); };
    lessThan = (y: number) => { return(this.value < y); };
    getValue = () => { return(this.value.toString()); }
}
export class DLOinterval<V, U extends DiscreteLinearOrder<any>>
implements DiscreteLinearOrder<number>{
    private start: U;
    private end: U;
    private data: any;
    private value: number;
    constructor(s: U, e: U, d: any,v:number) {
        this.value=v
        this.start = s;
        this.end   = e;
        this.data  = d;
    }
        next = () => { return(this.value + 2); };
        lessThan = (y: number) => { return(this.value < y); };
}



Make the test pass by changing the expected value!

Happy generic testing typsecript!

No comments: