Skip to content

Instantly share code, notes, and snippets.

@JohnWeisz
Created January 29, 2017 11:06
Show Gist options
  • Save JohnWeisz/beb7b4dadc512be30ce6c7c1e4c7e652 to your computer and use it in GitHub Desktop.
Save JohnWeisz/beb7b4dadc512be30ce6c7c1e4c7e652 to your computer and use it in GitHub Desktop.
TypeScript runtime checked type-assertion
export declare interface RuntimeAssert
{
any?: (value: any) => {
asNumber: () => number,
asArrayOfNumber: () => number[],
asString: () => string,
asArrayOfString: () => string[],
asBoolean: () => boolean,
asArrayOfBoolean: () => boolean[],
as: <T>(ctor: new (...args: any[]) => T) => T,
asArrayOf: <T>(ctor: new (...args: any[]) => T) => T[]
};
// Primitives.
(value: number): { asNumber: () => number };
(value: string): { asString: () => string };
(value: boolean): { asBoolean: () => boolean };
// Arrays of primitives.
(value: number[]): { asArrayOfNumber: () => number[] };
(value: string[]): { asArrayOfString: () => string[] };
(value: boolean[]): { asArrayOfBoolean: () => boolean[] };
// Unions of primitives.
(value: number | string): { asNumber: () => number, asString: () => string };
(value: number | boolean): { asNumber: () => number, asBoolean: () => boolean };
(value: string | boolean): { asString: () => string, asBoolean: () => boolean };
// Arrays of unions of primitives.
(value: (number | string)[]): { asArrayOfNumber: () => number[], asArrayOfString: () => string[] };
(value: (number | boolean)[]): { asArrayOfNumber: () => number[], asArrayOfBoolean: () => boolean[] };
(value: (boolean | string)[]): { asArrayOfBoolean: () => boolean[], asArrayOfString: () => string[] };
// Object values.
<T extends Function>(value: T): { as: (ctor: FunctionConstructor) => T };
<B>(value: B[]): {
asArrayOf: <T extends B>(ctor: new (...args: any[]) => T) => T[],
asArrayOfAbstract: (ctor: Function) => B[]
};
<B>(value: B): {
as: <T extends B>(ctor: new (...args: any[]) => T) => T,
asAbstract: (ctor: Function) => B
};
}
//var t1: EventTarget;
//var t2: EventTarget;
//var _t1 = assert(t1).as(HTMLInputElement);
//var _t2 = assert(t2).as(Date);
export var assert: RuntimeAssert = ((value: any): any =>
{
var _assertionObj = {
as: (ctor: new (...args: any[]) => any) =>
{
if (value === null)
return value;
if (typeof value === "undefined")
return value;
if (value instanceof ctor)
return value;
else
throw new TypeError(`Type '${acquireTypeName(value)}' is not assignable to type '${ctor.name}'.`);
},
asAbstract: (ctor: Function) =>
{
return _assertionObj.as(ctor as any);
},
asArrayOf: (ctor: new (...args: any[]) => any) =>
{
if (value instanceof Array)
{
if (value.length > 0 && value.some(item => !(item instanceof ctor)))
{
throw new TypeError(`Type '${acquireTypeName(value[0])}[]' is not assignable to type '${ctor.name}[]'.`);
}
return value;
}
else
{
throw new TypeError(`Type '${acquireTypeName(value)}' is not assignable to type '${ctor.name}[]'.`);
}
},
asNumber: () =>
{
if (value instanceof Number || typeof value === "number")
return value;
else if (typeof value === "undefined")
return 0;
else
throw new TypeError(`Type '${acquireTypeName(value)}' is not assignable to type 'number'.`);
},
asArrayOfNumber: () =>
{
if (value instanceof Array)
{
if (value.length > 0 && value.some(item => (!(item instanceof Number) && typeof item !== "number")))
{
throw new TypeError(`Type '${acquireTypeName(value[0])}[]' is not assignable to type 'number[]'.`);
}
return value;
}
else
{
throw new TypeError(`Type '${acquireTypeName(value)}' is not assignable to type 'number[]'.`);
}
},
asString: () =>
{
if (value instanceof String || typeof value === "string")
{
return value;
}
else if (typeof value === "undefined")
{
return "";
}
else
{
throw new TypeError(`Type '${acquireTypeName(value)}' is not assignable to type 'string'.`);
}
},
asArrayOfString: () =>
{
if (value instanceof Array)
{
if (value.length > 0 && value.some(item => (!(item instanceof String) && typeof item !== "string")))
{
throw new TypeError(`Type '${acquireTypeName(value[0])}[]' is not assignable to type 'string[]'.`);
}
return value;
}
else
{
throw new TypeError(`Type '${acquireTypeName(value)}' is not assignable to type 'string[]'.`);
}
},
asBoolean: () =>
{
if (value instanceof Boolean || typeof value === "boolean")
{
return value;
}
else if (typeof value === "undefined")
{
return false;
}
else
{
throw new TypeError(`Type '${acquireTypeName(value)}' is not assignable to type 'boolean'.`);
}
},
asArrayOfBoolean: () =>
{
if (value instanceof Array)
{
if (value.length > 0 && value.some(item => (!(item instanceof Boolean) && typeof item !== "boolean")))
{
throw new TypeError(`Type '${acquireTypeName(value[0])}[]' is not assignable to type 'boolean[]'.`);
}
return value;
}
else
{
throw new TypeError(`Type '${acquireTypeName(value)}' is not assignable to type 'boolean[]'.`);
}
}
};
return _assertionObj;
});
assert.any = assert as any;
function acquireTypeName(value: any)
{
var typeofValue = typeof value;
if (typeofValue === "object")
{
if (value === null)
{
return "null";
}
else
{
return value.constructor.name;
}
}
else
{
return typeofValue;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment