How to create TryCast in TypeScript (.Net Equivalent)

I'm surprised we didn't see much in the quoted TypeScript Docs . Trying to create a simple tryCast function in TypeScript similar to the one we have .Net. So, basically, try to apply to the specified type and if fail returns null / nothing. Perhaps it should use typescript generic <T> and how to make it work with objects in addition to primitive types?

function tryCast(obj: any, type: string) : any {
    if (typeof obj === type) {
        switch (type) {
            case 'number':
                return Number(obj);
            case 'string':
                return String(obj);
            case 'boolean':
                return Boolean(obj);
        }
    }

    return null;
}

function testCast() {
    var num: number = 123;  
    var str: string = tryCast(num, 'string');

    if (!str) {
        console.log('Cast failed');
    }
    else {
        console.log('Cast succeeded');
    }   
}

      

+3


source to share


2 answers


This is an interesting question. I can't think of a good scenario where this happens often enough to warrant overall functionality. In most cases, I would consider it on a case-by-case basis. Usually use typeof

for primitives and duck typing for custom types. However, this is a good exercise, so I'll give it a shot.

Your code does validation typeof

, but then it redundantly passes the variable to the constructor. If typeof obj === 'number'

, then you already have a number, there is no need to "throw". Below is a modified version of the answer given by @mallison . I also added classes for this solution, however the interfaces don't work as you might have hoped.

// Overloaded function
function tryCast(o: number, t: "number"): number;
function tryCast(o: string, t: "string"): string;
function tryCast(o: boolean, t: "boolean"): boolean;
function tryCast(o: IFace, t: "IFace"): IFace;
function tryCast(o: Function, t: "function"): Function;
function tryCast<T>(o: any, t: string): T;
function tryCast<T>(o: T, t: any): T {
     if (t === typeof o || (o['constructor'] && t === o['constructor']['name'])) {
        return o;
     } else  {
        return null;
     }
}

// Usage examples
var s1 = tryCast(70, 'string');
var s2 = tryCast('hello world', 'string');
var b1 = tryCast({b:true}, 'boolean');
var b2 = tryCast(true, 'boolean');
var n1 = tryCast('nan', 'number');
var n2 = tryCast(91, 'number');

var f1 = tryCast(123, 'function');
var f2 = tryCast(function foo() { return 1}, 'function');

class Classy { public sanDeigo = true; }
var c1 = tryCast<Classy>({soFly:false}, 'Classy');
var c2 = tryCast<Classy>(new Classy(), 'Classy');

interface IFace { eyes: number; hasMouth: boolean; }
var i1 = tryCast<IFace>({ eyes: 2, fake: true }, 'IFace');
var i2 = tryCast<IFace>({ eyes: 2, hasMouth: true }, 'IFace');

// Runtime tests
document.write(`
    s1:${s1},   // expect null<br/>
    s2:${s2},   // expect string<br/>
    b1:${b1},   // expect null<br/>
    b2:${b2},   // expect boolean<br/>
    n1:${n1},   // expect null<br/>
    n2:${n2},   // expect number<br/>
    f1:${f1},   // expect null<br/>
    f2:${f2},   // expect function<br/>
    c1:${c1},   // expect null<br/>
    c2:${c2},   // expect Classy<br/>
    i1:${i1},   // expect null<br/>
    i2:${i2},   // expect IFace...but its not!<br/>
`);

// Compiler tests
s1.toUpperCase();
s2.toUpperCase();

b1.valueOf();
b2.valueOf();

n1.toFixed(2);
n2.toFixed(2);

f1.apply(this);
f2.apply(this);

c1.sanDeigo;
c2.sanDeigo;

i1.eyes;
i2.eyes;

      

If you compile and run the code , you will see the following output:



s1:null, // expect null
s2:hello world, // expect string
b1:null, // expect null
b2:true, // expect boolean
n1:null, // expect null
n2:91, // expect number
f1:null,    // expect null
f2:function foo() { return 1; },    // expect function
c1:null,    // expect null
c2:[object Object], // expect Classy
i1:null,    // expect null
i2:null,    // expect IFace...but its not!

      

So what's going on? There is no generic way to cast an interface because it is a compile-time object. The runtime is unaware of interfaces. The class works because we know how TypeScript will compile the code, but that may not work in the future if the constructor name does not match the class name (you can check the ES6 spec before using this in production).

Again, the only way to check the type of an interface at runtime is duck, as I mentioned earlier. I'll leave you with what I think is the correct solution for a scenario where you would not know the type at compile time.

$.getJSON('data.json', function(data: IFace) {
    if (data && data.eyes > 0 && data.hasMouth) {
        // this looks like an IFace to me!
    } else {
        // data is no good!
    }
});

      

+3


source


Is this what you are looking for?



interface IFoo { bar: any; }

function tryCast(obj: number, type: "number"): number;
function tryCast(obj: string, type: "string"): string;
function tryCast(obj: boolean, type: "boolean"): boolean;
function tryCast(obj: IFoo, type: "IFoo"): IFoo;
function tryCast(obj: any, type: string): any;
function tryCast(obj: any, type: string): any {

    // Do fancy stuff to cast things
}

// IFoo
tryCast({ bar: 'hi' }, 'IFoo');

// any (NULL)
tryCast({}, 'string');

// boolean
tryCast( !!1 , 'boolean');

// any (NULL)
tryCast(123, 'boolean');

// number
tryCast(123, 'number');

// string
tryCast( 123 + '', 'string');

      

+1


source







All Articles