Proposal-pattern-matching: Match on types

Created on 12 Dec 2018  路  11Comments  路  Source: tc39/proposal-pattern-matching

It would be nice to support matching on types:

let myVar = ...
case (myVar) {
    when Number -> 'A number'
    when String -> 'A string'
    when Array -> 'A list'
    when Object -> 'An object'
    when undefined -> 'Nothing'
    when _ -> 'Something else'
  }

Most helpful comment

Also, what if myVar is the actual Number constructor?

All 11 comments

3 instanceof Number is false, how would you describe the comparison being applied here? How would that extend to custom constructors?

3 instanceof Number is false,

instanceof is what I had in mind, but your counter example shows its limitations

instanceof also doesn鈥檛 work cross-realm, so it might not be the best default mechanism for any matching.

Maybe this is a follow-on proposal (and I feel like I may have see this proposed somewhere), but seems like a good place for a "Symbol-method" solution?

Another use case for @mAAdhaTTah鈥檚 idea of Symbol.whenPredicate (or similar): matching a string against regular expressions.

i'm not sure the suggestion will improve ease-of-debugging (or prevent footguns) over just explicitly matching typeof:

/*jslint devel*/
"use strict";
function foo(myVar) {
    switch (typeof myVar) {
    case "number":
        return "A number";
    case "object":
        if (Array.isArray(myVar)) {
            return "A list";
        }
        return "An object";
    case "string":
        return "A string";
    case "undefined":
        return "Nothing";
    default:
        return "Something else";
    }
}
console.assert(foo(1) === "A number"); // true
console.assert(foo([]) === "A list"); // true
console.assert(foo({}) === "An object"); // true
console.assert(foo(null) === "An object"); // true
console.assert(foo("hello") === "A string"); // true
console.assert(foo(undefined) === "Nothing"); // true
console.assert(foo(Symbol()) === "Something else"); // true

I'm not sure how one would disambiguate between when TypeName and when identifier (with the latter matching anything and binding it to the identifier in the scope of that branch).

I.e., why wouldn't when Number -> match anything and bind it to Number, or conversely, why wouldn't when _ -> try to match against a type _ (which is a perfectly fine identifier)?

Also, what if myVar is the actual Number constructor?

In this case, why not match for Object.constructor?

let myVar = ...
case (myVar) {
    when { constructor: Number } -> 'A number'
    when { constructor: String } -> 'A string'
    when { constructor: Array } -> 'A list'
    when { constructor: Object } -> 'An object'
    when undefined -> 'Nothing'
    when _ -> 'Something else'
}

Or more readable:

let myVar = ...
case (myVar.constructor) {
    when Number -> 'A number'
    when String -> 'A string'
    when Array -> 'A list'
    when Object -> 'An object'
    when undefined -> 'Nothing'
    when _ -> 'Something else'
}

Or including value access while readable:

let myVar = ...
case ([myVar.constructor, myVar]) {
    when [Number, _] -> 'A number'
    when [String, _] -> 'A string'
    when [Array, _] -> 'A list'
    when [Object, _] -> 'An object'
    when undefined -> 'Nothing'
    when _ -> 'Something else'
}

@JorisBlanken because constructor matching is very unreliable and doesn't work across realms. It's the reason Array.isArray was created, and one should always use it over instanceof Array (and why instanceof should also be avoided)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tabatkins picture tabatkins  路  6Comments

samuelgruetter picture samuelgruetter  路  3Comments

caub picture caub  路  8Comments

mheiber picture mheiber  路  5Comments

MoritzKn picture MoritzKn  路  5Comments