diff --git a/src/Core__Type.mjs b/src/Core__Type.mjs index 8ea2b647..279cbd4d 100644 --- a/src/Core__Type.mjs +++ b/src/Core__Type.mjs @@ -1,53 +1,255 @@ // Generated by ReScript, PLEASE EDIT WITH CARE +import * as Caml_option from "rescript/lib/es6/caml_option.js"; + +function isNull(i) { + return i === null; +} + +function isNullOrUndefined(i) { + if (i === null) { + return true; + } else { + return i === undefined; + } +} + +function isUndefined(i) { + return i === undefined; +} function classify(value) { - var match = Object.prototype.toString.call(value); - switch (match) { - case "[object Boolean]" : - return { - TAG: /* Bool */0, - _0: value - }; - case "[object AsyncFunction]" : - case "[object Function]" : - case "[object GeneratorFunction]" : - return { - TAG: /* Function */4, - _0: value - }; - case "[object Null]" : - return /* Null */0; - case "[object Number]" : - return { - TAG: /* Number */2, - _0: value - }; - case "[object String]" : - return { - TAG: /* String */1, - _0: value - }; - case "[object Symbol]" : - return { - TAG: /* Symbol */5, - _0: value - }; - case "[object Undefined]" : - return /* Undefined */1; - default: + var match = typeof value; + if (match === "symbol") { + return { + TAG: /* Symbol */5, + _0: value + }; + } else if (match === "boolean") { + return { + TAG: /* Bool */1, + _0: value + }; + } else if (match === "string") { + return { + TAG: /* String */4, + _0: value + }; + } else if (match === "function") { + return { + TAG: /* Function */6, + _0: value + }; + } else if (match === "object") { + if (value === null) { + return /* Null */1; + } else { return { - TAG: /* Object */3, + TAG: /* Object */0, _0: value }; + } + } else if (match === "undefined") { + return /* Undefined */0; + } else if (match === "number") { + return { + TAG: /* Number */2, + _0: value + }; + } else { + return { + TAG: /* BigInt */3, + _0: value + }; + } +} + +function toObject(i) { + if (typeof i === "object") { + return Caml_option.some(i); } + } -var Classify = { - classify: classify -}; +function toBool(i) { + if (typeof i === "boolean") { + return i; + } + +} + +function toFloat(i) { + if (typeof i === "number") { + return i; + } + +} + +function toBigInt(i) { + if (typeof i === "bigint") { + return Caml_option.some(i); + } + +} + +function toString(i) { + if (typeof i === "string") { + return i; + } + +} + +function toSymbol(i) { + if (typeof i === "symbol") { + return Caml_option.some(i); + } + +} + +function toFunction(i) { + if (typeof i === "function") { + return Caml_option.some(i); + } + +} + +function getObject(o, n) { + if (isNullOrUndefined(o)) { + return ; + } else { + return toObject(o[n]); + } +} + +function getObjectBySymbol(o, s) { + if (isNullOrUndefined(o)) { + return ; + } else { + return toObject(o[s]); + } +} + +function getBool(o, n) { + if (isNullOrUndefined(o)) { + return ; + } else { + return toBool(o[n]); + } +} + +function getBoolBySymbol(o, s) { + if (isNullOrUndefined(o)) { + return ; + } else { + return toBool(o[s]); + } +} + +function getFloat(o, n) { + if (isNullOrUndefined(o)) { + return ; + } else { + return toFloat(o[n]); + } +} + +function getFloatBySymbol(o, s) { + if (isNullOrUndefined(o)) { + return ; + } else { + return toFloat(o[s]); + } +} + +function getBigInt(o, n) { + if (isNullOrUndefined(o)) { + return ; + } else { + return toBigInt(o[n]); + } +} + +function getBigIntBySymbol(o, s) { + if (isNullOrUndefined(o)) { + return ; + } else { + return toBigInt(o[s]); + } +} + +function getString(o, n) { + if (isNullOrUndefined(o)) { + return ; + } else { + return toString(o[n]); + } +} + +function getStringBySymbol(o, s) { + if (isNullOrUndefined(o)) { + return ; + } else { + return toString(o[s]); + } +} + +function getSymbol(o, n) { + if (isNullOrUndefined(o)) { + return ; + } else { + return toSymbol(o[n]); + } +} + +function getSymbolBySymbol(o, s) { + if (isNullOrUndefined(o)) { + return ; + } else { + return toSymbol(o[s]); + } +} + +function getFunction(o, n) { + if (isNullOrUndefined(o)) { + return ; + } else { + return toFunction(o[n]); + } +} + +function getFunctionBySymbol(o, s) { + if (isNullOrUndefined(o)) { + return ; + } else { + return toFunction(o[s]); + } +} export { - Classify , + classify , + isUndefined , + isNull , + isNullOrUndefined , + toObject , + toBool , + toFloat , + toBigInt , + toString , + toSymbol , + toFunction , + getObject , + getObjectBySymbol , + getBool , + getBoolBySymbol , + getFloat , + getFloatBySymbol , + getBigInt , + getBigIntBySymbol , + getString , + getStringBySymbol , + getSymbol , + getSymbolBySymbol , + getFunction , + getFunctionBySymbol , } /* No side effect */ diff --git a/src/Core__Type.res b/src/Core__Type.res index aaea1806..57b0514d 100644 --- a/src/Core__Type.res +++ b/src/Core__Type.res @@ -1,43 +1,96 @@ -type t = [#undefined | #object | #boolean | #number | #bigint | #string | #symbol | #function] - -external typeof: 'a => t = "#typeof" - -module Classify = { - type function - type object - type symbol - - type t = - | Bool(bool) - | Null - | Undefined - | String(string) - | Number(float) - | Object(object) - | Function(function) - | Symbol(symbol) - - @val external _internalClass: 'a => string = "Object.prototype.toString.call" - external _asBool: 'a => bool = "%identity" - external _asString: 'a => string = "%identity" - external _asFloat: 'a => float = "%identity" - external _asObject: 'a => object = "%identity" - external _asFunction: 'a => function = "%identity" - external _asSymbol: 'a => symbol = "%identity" - - let classify = value => { - switch _internalClass(value) { - | "[object Boolean]" => Bool(_asBool(value)) - | "[object Null]" => Null - | "[object Undefined]" => Undefined - | "[object String]" => String(_asString(value)) - | "[object Number]" => Number(_asFloat(value)) - | "[object Function]" - | "[object GeneratorFunction]" - | "[object AsyncFunction]" => - Function(_asFunction(value)) - | "[object Symbol]" => Symbol(_asSymbol(value)) - | _ => Object(_asObject(value)) +type typeof = [ + | #undefined + | #object + | #boolean + | #number + | #bigint + | #string + | #symbol + | #function +] + +external typeof: 'a => typeof = "#typeof" + +type unknown +external toUnknown: 'a => unknown = "%identity" + +type object // How to use with the Object module? +type function + +// typeof null == "object". Gotta love that! Do better here. +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof#typeof_null +type jsType = + | Undefined + | Null + | Object(object) + | Bool(bool) + | Number(float) + | BigInt(Core__BigInt.t) + | String(string) + | Symbol(Core__Symbol.t) + | Function(function) + +external toObjectUnsafe: 'a => object = "%identity" +external toBoolUnsafe: 'a => bool = "%identity" +external toFloatUnsafe: 'a => float = "%identity" +external toBigIntUnsafe: 'a => Core__BigInt.t = "%identity" +external toStringUnsafe: 'a => string = "%identity" +external toSymbolUnsafe: 'a => Core__Symbol.t = "%identity" +external toFunctionUnsafe: 'a => function = "%identity" + +module N = Core__Nullable +let isNull = i => N.make(i) == N.null +let isNullOrUndefined = i => N.make(i) == N.null || N.make(i) == N.undefined +let isUndefined = i => N.make(i) == N.undefined + +let classify = value => { + switch typeof(value) { + | #number => value->toFloatUnsafe->Number + | #string => value->toStringUnsafe->String + | #boolean => value->toBoolUnsafe->Bool + | #undefined => Undefined + | #function => value->toFunctionUnsafe->Function + | #bigint => value->toBigIntUnsafe->BigInt + | #symbol => value->toSymbolUnsafe->Symbol + | #object => + switch isNull(value) { + | true => Null + | false => value->toObjectUnsafe->Object } } } + +let toObject = i => typeof(i) === #object ? Some((Obj.magic(i): object)) : None +let toBool = i => typeof(i) === #boolean ? Some((Obj.magic(i): bool)) : None +let toFloat = i => typeof(i) === #number ? Some((Obj.magic(i): float)) : None +let toBigInt = i => typeof(i) === #bigint ? Some((Obj.magic(i): Core__BigInt.t)) : None +let toString = i => typeof(i) === #string ? Some((Obj.magic(i): string)) : None +let toSymbol = i => typeof(i) === #symbol ? Some((Obj.magic(i): Core__Symbol.t)) : None +let toFunction = i => typeof(i) === #function ? Some((Obj.magic(i): function)) : None + +// Implicitly creates a wrapper object for primitives that is promptly discard. +// Throws if the object is null or undefined. +@get_index external getUnsafe: ('a, string) => unknown = "" +@get_index external getBySymbolUnsafe: ('a, Core__Symbol.t) => unknown = "" + +let getObject = (o, n) => isNullOrUndefined(o) ? None : o->getUnsafe(n)->toObject +let getObjectBySymbol = (o, s) => isNullOrUndefined(o) ? None : o->getBySymbolUnsafe(s)->toObject + +let getBool = (o, n) => isNullOrUndefined(o) ? None : o->getUnsafe(n)->toBool +let getBoolBySymbol = (o, s) => isNullOrUndefined(o) ? None : o->getBySymbolUnsafe(s)->toBool + +let getFloat = (o, n) => isNullOrUndefined(o) ? None : o->getUnsafe(n)->toFloat +let getFloatBySymbol = (o, s) => isNullOrUndefined(o) ? None : o->getBySymbolUnsafe(s)->toFloat + +let getBigInt = (o, n) => isNullOrUndefined(o) ? None : o->getUnsafe(n)->toBigInt +let getBigIntBySymbol = (o, s) => isNullOrUndefined(o) ? None : o->getBySymbolUnsafe(s)->toBigInt + +let getString = (o, n) => isNullOrUndefined(o) ? None : o->getUnsafe(n)->toString +let getStringBySymbol = (o, s) => isNullOrUndefined(o) ? None : o->getBySymbolUnsafe(s)->toString + +let getSymbol = (o, n) => isNullOrUndefined(o) ? None : o->getUnsafe(n)->toSymbol +let getSymbolBySymbol = (o, s) => isNullOrUndefined(o) ? None : o->getBySymbolUnsafe(s)->toSymbol + +let getFunction = (o, n) => isNullOrUndefined(o) ? None : o->getUnsafe(n)->toFunction +let getFunctionBySymbol = (o, s) => + isNullOrUndefined(o) ? None : o->getBySymbolUnsafe(s)->toFunction diff --git a/src/Core__Type.resi b/src/Core__Type.resi index 8bfe4bdf..b2a96266 100644 --- a/src/Core__Type.resi +++ b/src/Core__Type.resi @@ -1,83 +1,124 @@ /*** -Utilities for classifying the type of JavaScript values at runtime. +Utilities for classifying and safely using JavaScript values at runtime. */ /** -The possible types of JavaScript values. +The possible types of JavaScript values. See [typeof on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof) */ -type t = [#undefined | #object | #boolean | #number | #bigint | #string | #symbol | #function] +type typeof = [ + | #undefined + | #object + | #boolean + | #number + | #bigint + | #string + | #symbol + | #function +] /** -`typeof(someValue)` +An abstract representation for values whose contents are a mystery, or at least not 100% guaranteed to be what you expect. A value of type `unknown` could be undefined, null, a number, string, function, or any other JavaScript type. To safely use an `unknown` value use the functions in this module to test and classify it as its true underlying type. +*/ +type unknown -Returns the underlying JavaScript type of any runtime value. +/** +`toUnknown` convert any type to `unknown`, forcing consumers of that value to test its contents before using it. +*/ +external toUnknown: 'a => unknown = "%identity" -See [`typeof`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof) on MDN. +/** +`typeof(value)` returns the underlying JavaScript type of `value` at runtime. **Note** null values return `object`. See [typeof on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof) ## Examples ```rescript -Console.log(Type.typeof("Hello")) // Logs "string" to the console. - -let someVariable = true - -switch someVariable->Type.typeof { -| #boolean => Console.log("This is a bool, yay!") -| _ => Console.log("Oh, not a bool sadly...") -} +Type.typeof("Hello") // "string" +Type.typeof(3) // "number" +Type.typeof(3.14) // "number" +Type.typeof(null) // "object" ``` */ -external typeof: 'a => t = "#typeof" - -module Classify: { - /*** - Classifies JavaScript runtime values. - */ - - /** - An abstract type representing a JavaScript function. +external typeof: 'a => typeof = "#typeof" - See [`function`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) on MDN. - */ - type function - - /** - An abstract type representing a JavaScript object. +// Why can't this be {..} so it can be used by the Object module? +type object - See [`object`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) on MDN. +/** +An abstract type representing a JavaScript function. See [`function on MDN`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) */ - type object +type function - /** - An abstract type representing a JavaScript symbol. - - See [`symbol`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol) on MDN. - */ - type symbol +/** +Represents a classified JavaScript value. **Note:** Unlike ReScript, JavaScript does not distinguish between floats and ints. A classified `float` can be later converted to an `int` if desired. +*/ +type jsType = + | Undefined + | Null + | Object(object) + | Bool(bool) + | Number(float) + | BigInt(Core__BigInt.t) + | String(string) + | Symbol(Core__Symbol.t) + | Function(function) - /** - The type representing a classified JavaScript value. - */ - type t = - | Bool(bool) - | Null - | Undefined - | String(string) - | Number(float) - | Object(object) - | Function(function) - | Symbol(symbol) - - /** -`classify(anyValue)` -Classifies a JavaScript value. +/** +`classify(value)` inspects a value and determines its type. ## Examples ```rescript -switch %raw(`null`)->Type.Classify.classify { +switch %raw(`null`)->classify { | Null => Console.log("Yup, that's null.") | _ => Console.log("This doesn't actually appear to be null...") } ``` */ - let classify: 'a => t -} +let classify: 'a => jsType + +let isUndefined: 'a => bool +let isNull: 'a => bool +let isNullOrUndefined: 'a => bool + +// Safely converts any type to the desigated type. Simply a convience wrapper around the `classify` function. +let toObject: 'a => option +let toBool: 'a => option +let toFloat: 'a => option +let toBigInt: 'a => option +let toString: 'a => option +let toSymbol: 'a => option +let toFunction: 'a => option + +// Unsafely converts any type to the designated type. +external toObjectUnsafe: 'a => object = "%identity" +external toBoolUnsafe: 'a => bool = "%identity" +external toFloatUnsafe: 'a => float = "%identity" +external toBigIntUnsafe: 'a => Core__BigInt.t = "%identity" +external toStringUnsafe: 'a => string = "%identity" +external toSymbolUnsafe: 'a => Core__Symbol.t = "%identity" +external toFunctionUnsafe: 'a => function = "%identity" + +// Safely access properties on unknown types either by name or symbol. These +// have safety checks: (a) The object is not null or undefined, and (b) the +// return value must be what the user expects it to be. These functions don't +// distinguish between a property that exists with an undefined or null value, +// or one where the property does not exist at all. + +let getObject: ('a, string) => option +let getObjectBySymbol: ('a, Core__Symbol.t) => option + +let getBool: ('a, string) => option +let getBoolBySymbol: ('a, Core__Symbol.t) => option + +let getFloat: ('a, string) => option +let getFloatBySymbol: ('a, Core__Symbol.t) => option + +let getBigInt: ('a, string) => option +let getBigIntBySymbol: ('a, Core__Symbol.t) => option + +let getString: ('a, string) => option +let getStringBySymbol: ('a, Core__Symbol.t) => option + +let getSymbol: ('a, string) => option +let getSymbolBySymbol: ('a, Core__Symbol.t) => option + +let getFunction: ('a, string) => option +let getFunctionBySymbol: ('a, Core__Symbol.t) => option diff --git a/src/RescriptCore.res b/src/RescriptCore.res index 74fdad58..862d5a02 100644 --- a/src/RescriptCore.res +++ b/src/RescriptCore.res @@ -49,7 +49,7 @@ module Intl = Core__Intl external null: Core__Nullable.t<'a> = "#null" external undefined: Core__Nullable.t<'a> = "#undefined" -external typeof: 'a => Core__Type.t = "#typeof" +external typeof: 'a => Core__Type.typeof = "#typeof" type t<'a> = Js.t<'a> module MapperRt = Js.MapperRt