- can't decide between different array types (solution idea: if the reference array is not empty, test the first element against the first element of the test element)
Last active
December 21, 2016 15:39
-
-
Save DevWurm/51b60a678ef5aa561078df34009738c3 to your computer and use it in GitHub Desktop.
A simple generic type matching / constructor matching type guard for type script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// import match.ts | |
// weired types | |
class Nums { | |
a: number = 2 | |
b(): number { | |
return 7; | |
} | |
} | |
type numeric = number | number[] | Nums | |
// function for extracting a basic number from a numeric type | |
function extract(n: numeric): number { | |
// matching by type constructor | |
if (match(n, Array)) { | |
return n[0]; | |
} | |
// matching by type compliant literal (possible, but not reccomended in favot of readability) | |
else if (match(n, 7)) { | |
return n; | |
} | |
// matching by type complienat object literal | |
else if (match(n, { a: 1, b: () => 7 })) { | |
return n.a + n.b(); | |
} | |
else { | |
return NaN; | |
} | |
} | |
alert(`Expecting 7.123: ${extract(7.123)}`); | |
alert(`Expecting 4: ${extract([4, 3, 2, 1])}`); | |
alert(`Expecting 9: ${extract(new Nums())}`); | |
alert(`Expecting 3: ${extract({ a: 0, b: () => 3 })}`); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function match<T>(o: any, ref: {new(...args : any[]): T} | T): o is T { | |
let oObject: Object; | |
switch (typeof o) { | |
case 'boolean': oObject = new Boolean(o); break; | |
case 'number': oObject = new Number(o); break; | |
case 'string': oObject = new String(o); break; | |
case 'undefined': return ref === undefined; | |
default: oObject = o; | |
} | |
if (isConstructor(ref)) { | |
return ( | |
oObject instanceof ref | |
|| | |
Object.getOwnPropertyNames(ref.prototype) | |
.reduce((acc, curr) => (curr in oObject) && acc, true) | |
) | |
} else { | |
return ( | |
Object.getOwnPropertyNames(Object.getPrototypeOf(ref)) | |
.reduce((acc, curr) => (curr in oObject) && acc, true) | |
) | |
} | |
} | |
function isConstructor<T>(x: { new (...args : any[]): T } | T): x is { new (): T } { | |
return Boolean((<{ new (): T }> x).prototype); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Live example: http://www.typescriptlang.org/play/index.html#src=function%20match%3CT%3E(o%3A%20any%2C%20ref%3A%20%7Bnew()%3A%20T%7D%20%7C%20T)%3A%20o%20is%20T%20%20%7B%0D%0A%20%20%20%20let%20oObject%3A%20Object%3B%0D%0A%20%20%20%20switch%20(typeof%20o)%20%7B%0D%0A%20%20%20%20%20%20%20%20case%20%27boolean%27%3A%20oObject%20%3D%20new%20Boolean(o)%3B%20break%3B%0D%0A%20%20%20%20%20%20%20%20case%20%27number%27%3A%20oObject%20%3D%20new%20Number(o)%3B%20break%3B%0D%0A%20%20%20%20%20%20%20%20case%20%27string%27%3A%20oObject%20%3D%20new%20String(o)%3B%20break%3B%0D%0A%20%20%20%20%20%20%20%20case%20%27undefined%27%3A%20return%20ref%20%3D%3D%3D%20undefined%3B%0D%0A%20%20%20%20%20%20%20%20default%3A%20oObject%20%3D%20o%3B%0D%0A%20%20%20%20%7D%0D%0A%0D%0A%20%20%20%20if%20(isConstructor(ref))%20%7B%0D%0A%20%20%20%20%20%20%20%20return%20(%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20oObject%20instanceof%20ref%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20Object.getOwnPropertyNames(ref.prototype)%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.reduce((acc%2C%20curr)%20%3D%3E%20(curr%20in%20oObject)%20%26%26%20acc%2C%20true)%0D%0A%20%20%20%20%20%20%20%20)%0D%0A%20%20%20%20%7D%20else%20%7B%0D%0A%20%20%20%20%20%20%20%20return%20(%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20Object.getOwnPropertyNames(Object.getPrototypeOf(ref))%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.reduce((acc%2C%20curr)%20%3D%3E%20(curr%20in%20oObject)%20%26%26%20acc%2C%20true)%0D%0A%20%20%20%20%20%20%20%20)%0D%0A%20%20%20%20%7D%0D%0A%0D%0A%7D%0D%0A%0D%0Afunction%20isConstructor%3CT%3E(x%3A%20%7B%20new%20()%3A%20T%20%7D%20%7C%20T)%3A%20x%20is%20%7B%20new%20()%3A%20T%20%7D%20%7B%0D%0A%20%20%20%20return%20Boolean((%3C%7B%20new%20()%3A%20T%20%7D%3E%20x).prototype)%3B%0D%0A%7D%0D%0A%0D%0A%2F%2F%20weired%20types%0D%0Aclass%20Nums%20%7B%0D%0A%20%20%20%20a%3A%20number%20%3D%202%0D%0A%20%20%20%20b()%3A%20number%20%7B%0D%0A%20%20%20%20%20%20%20%20return%207%3B%0D%0A%20%20%20%20%7D%0D%0A%7D%0D%0A%0D%0Atype%20numeric%20%3D%20number%20%7C%20number%5B%5D%20%7C%20Nums%0D%0A%0D%0A%2F%2F%20function%20for%20extracting%20a%20basic%20number%20from%20a%20numeric%20type%0D%0Afunction%20extract(n%3A%20numeric)%3A%20number%20%7B%0D%0A%20%20%20%20%2F%2F%20matching%20by%20type%20constructor%0D%0A%20%20%20%20if%20(match(n%2C%20Array))%20%7B%0D%0A%20%20%20%20%20%20%20%20return%20n%5B0%5D%3B%0D%0A%20%20%20%20%7D%0D%0A%20%20%20%20%2F%2F%20matching%20by%20type%20compliant%20literal%20(possible%2C%20but%20not%20reccomended%20in%20favot%20of%20readability)%0D%0A%20%20%20%20else%20if%20(match(n%2C%207))%20%7B%0D%0A%20%20%20%20%20%20%20%20return%20n%3B%0D%0A%20%20%20%20%7D%0D%0A%20%20%20%20%2F%2F%20matching%20by%20type%20complienat%20object%20literal%0D%0A%20%20%20%20else%20if%20(match(n%2C%20%7B%20a%3A%201%2C%20b%3A%20()%20%3D%3E%207%20%7D))%20%7B%0D%0A%20%20%20%20%20%20%20%20return%20n.a%20%2B%20n.b()%3B%0D%0A%20%20%20%20%7D%0D%0A%20%20%20%20else%20%7B%0D%0A%20%20%20%20%20%20%20%20return%20NaN%3B%0D%0A%20%20%20%20%7D%0D%0A%7D%0D%0A%0D%0Aalert(%60Expecting%207.123%3A%20%24%7Bextract(7.123)%7D%60)%3B%0D%0Aalert(%60Expecting%204%3A%20%24%7Bextract(%5B4%2C%203%2C%202%2C%201%5D)%7D%60)%3B%0D%0Aalert(%60Expecting%209%3A%20%24%7Bextract(new%20Nums())%7D%60)%3B%0D%0Aalert(%60Expecting%203%3A%20%24%7Bextract(%7B%20a%3A%200%2C%20b%3A%20()%20%3D%3E%203%20%7D)%7D%60)%3B