Comparing version 1.0.3 to 1.1.0
134
can-type.js
@@ -129,62 +129,2 @@ var canReflect = require("can-reflect"); | ||
function makeCache(fn) { | ||
var cache = new WeakMap(); | ||
return function(Type) { | ||
if(cache.has(Type)) { | ||
return cache.get(Type); | ||
} | ||
var typeObject = fn.call(this, Type); | ||
cache.set(Type, typeObject); | ||
return typeObject; | ||
}; | ||
} | ||
exports.check = makeCache(function(Type) { | ||
var o = Object.create(getBaseType(Type)); | ||
o[newSymbol] = strictNew; | ||
inheritFrom(o, Type, isMemberSymbol); | ||
inheritFrom(o, Type, getSchemaSymbol); | ||
canReflect.setName(o, wrapName("check", Type)); | ||
return o; | ||
}); | ||
exports.convert = makeCache(function(Type) { | ||
var o = Object.create(getBaseType(Type)); | ||
inheritFrom(o, Type, isMemberSymbol); | ||
inheritFrom(o, Type, getSchemaSymbol); | ||
canReflect.setName(o, wrapName("convert", Type)); | ||
return o; | ||
}); | ||
exports.maybe = makeCache(function(Type) { | ||
var baseType = getBaseType(Type); | ||
var desc = {}; | ||
desc[newSymbol] = { | ||
value: strictNew | ||
}; | ||
desc[isMemberSymbol] = { | ||
value: makeMaybe(baseType) | ||
}; | ||
desc[getSchemaSymbol] = { | ||
value: makeMaybeSchema(baseType) | ||
}; | ||
var o = Object.create(baseType, desc); | ||
canReflect.setName(o, wrapName("maybe", Type)); | ||
return o; | ||
}); | ||
exports.maybeConvert = makeCache(function(Type) { | ||
var baseType = getBaseType(Type); | ||
var desc = {}; | ||
desc[isMemberSymbol] = { | ||
value: makeMaybe(baseType) | ||
}; | ||
desc[getSchemaSymbol] = { | ||
value: makeMaybeSchema(baseType) | ||
}; | ||
var o = Object.create(baseType, desc); | ||
canReflect.setName(o, wrapName("maybeConvert", Type)); | ||
return o; | ||
}); | ||
function isTypeObject(Type) { | ||
@@ -259,2 +199,74 @@ if(canReflect.isPrimitive(Type)) { | ||
var Integer = {}; | ||
Integer[newSymbol] = function(value) { | ||
return parseInt(value); | ||
}; | ||
Integer[isMemberSymbol] = function(value) { | ||
// “polyfill” for Number.isInteger because it’s not supported in IE11 | ||
return typeof value === "number" && isFinite(value) && | ||
Math.floor(value) === value; | ||
}; | ||
Integer[getSchemaSymbol] = makeSchema([Integer]); | ||
canReflect.setName(Integer, "Integer"); | ||
function makeCache(fn) { | ||
var cache = new WeakMap(); | ||
return function(Type) { | ||
if(cache.has(Type)) { | ||
return cache.get(Type); | ||
} | ||
var typeObject = fn.call(this, Type); | ||
cache.set(Type, typeObject); | ||
return typeObject; | ||
}; | ||
} | ||
exports.check = makeCache(function(Type) { | ||
var o = Object.create(getBaseType(Type)); | ||
o[newSymbol] = strictNew; | ||
inheritFrom(o, Type, isMemberSymbol); | ||
inheritFrom(o, Type, getSchemaSymbol); | ||
canReflect.setName(o, wrapName("check", Type)); | ||
return o; | ||
}); | ||
exports.convert = makeCache(function(Type) { | ||
var o = Object.create(getBaseType(Type)); | ||
inheritFrom(o, Type, isMemberSymbol); | ||
inheritFrom(o, Type, getSchemaSymbol); | ||
canReflect.setName(o, wrapName("convert", Type)); | ||
return o; | ||
}); | ||
exports.maybe = makeCache(function(Type) { | ||
var baseType = getBaseType(Type); | ||
var desc = {}; | ||
desc[newSymbol] = { | ||
value: strictNew | ||
}; | ||
desc[isMemberSymbol] = { | ||
value: makeMaybe(baseType) | ||
}; | ||
desc[getSchemaSymbol] = { | ||
value: makeMaybeSchema(baseType) | ||
}; | ||
var o = Object.create(baseType, desc); | ||
canReflect.setName(o, wrapName("maybe", Type)); | ||
return o; | ||
}); | ||
exports.maybeConvert = makeCache(function(Type) { | ||
var baseType = getBaseType(Type); | ||
var desc = {}; | ||
desc[isMemberSymbol] = { | ||
value: makeMaybe(baseType) | ||
}; | ||
desc[getSchemaSymbol] = { | ||
value: makeMaybeSchema(baseType) | ||
}; | ||
var o = Object.create(baseType, desc); | ||
canReflect.setName(o, wrapName("maybeConvert", Type)); | ||
return o; | ||
}); | ||
// type checking should not throw in production | ||
@@ -267,2 +279,4 @@ if(process.env.NODE_ENV === 'production') { | ||
exports.Any = Any; | ||
exports.Integer = Integer; | ||
exports.late = late; | ||
@@ -269,0 +283,0 @@ exports.isTypeObject = isTypeObject; |
@@ -10,3 +10,3 @@ @function can-type/all all | ||
```js | ||
import { ObservableObject, Reflect, type } from "can/everything"; | ||
import { ObservableObject, Reflect, type } from "can"; | ||
@@ -13,0 +13,0 @@ class Person extends ObservableObject { |
@property {can-type.typeobject} can-type.any Any | ||
@parent can-type/types | ||
@parent can-type/types 0 | ||
@description The `Any` type represents any type. | ||
@@ -10,15 +10,22 @@ | ||
```js | ||
import { Reflect, type } from "can/everything"; | ||
import { ObservableObject, type } from "can"; | ||
let val = Reflect.convert(42, type.Any); | ||
console.log(val); // -> 42 | ||
class EnvironmentVariable extends ObservableObject { | ||
static props = { | ||
value: type.Any | ||
}; | ||
} | ||
val = Reflect.convert(null, type.Any); | ||
console.log(val); // -> null | ||
let env = new EnvironmentVariable(); | ||
env.value = 42; | ||
console.log(env.value); // -> 42 | ||
val = Reflect.convert([], type.Any); | ||
console.log(val); // -> [] | ||
env.value = null; | ||
console.log(env.value); // -> null | ||
val = Reflect.convert(new Date(), type.Any); | ||
console.log(val); // Date() | ||
env.value = []; | ||
console.log(env.value); // -> [] | ||
env.value = new Date(); | ||
console.log(env.value); // -> Date | ||
``` | ||
@@ -30,9 +37,18 @@ @codepen | ||
```js | ||
import { Reflect, type } from "can/everything"; | ||
import { ObservableObject, type } from "can"; | ||
class Schedule extends ObservableObject { | ||
static props = { | ||
firstPeriod: type.Any | ||
}; | ||
} | ||
let today = new Date(); | ||
let sched = new Schedule(); | ||
sched.firstPeriod = today; | ||
let val = Reflect.convert(today, type.Any); | ||
console.log(val); // today | ||
console.log(sched.firstPeriod === today); // -> true | ||
console.log(sched.firstPeriod); // -> today | ||
``` | ||
@codepen |
@@ -9,91 +9,143 @@ @module {Object} can-type | ||
@description Define types that can verify values are of the correct type, or convert values to the correct type. | ||
@type {Object} | ||
Exports an object with methods related to type operations like checking and conversion. The following describes the properties and methods on the export. | ||
```js | ||
{ | ||
check, // Create a type that throws if | ||
// a value of another type is passed. | ||
convert, // Create a type that will convert a | ||
// value to that type. | ||
maybe, // Create a type that accepts the type, | ||
// null, or undefined. | ||
maybeConvert // Create a type that will convert a value, | ||
// unless null or undefined. | ||
Any, // A type that represents any type. | ||
late, // Define a function that will return a | ||
// type later. | ||
convertAll, // Create type where all properties are | ||
// converted to their given type. | ||
isTypeObject // Test if an object is a typeObject known | ||
// to can-type's type system. | ||
} | ||
``` | ||
@body | ||
## Overview | ||
## Use Cases | ||
Use can-type to define rules around types to handle type checking and type conversion. Works well with [can-define], [can-observable-object], and [can-stache-element]. | ||
Use can-type to define rules around types to handle type checking and type conversion. Works well with [can-observable-object], and [can-stache-element], [can-define]. | ||
can-type specifies the following type functions: | ||
can-type works well for the following scenarios: | ||
### type.maybe | ||
### Prevent wrong types from being passed | ||
Use [can-type/maybe] to specify a [can-type.typeobject] which will accept a value that is a member of the provided type, or is `undefined` or `null`. | ||
Using [can-type/check] you can ensure that properties of the wrong type are not passed to your components. If a value of any other type is passed it will throw (in development mode) and let the developer know the mistake they made. | ||
```js | ||
import { Reflect, type } from "can"; | ||
import { ObservableObject, type } from "can"; | ||
const NumberType = type.maybe(Number); | ||
class Person extends ObservableObject { | ||
static props = { | ||
name: type.check(String) | ||
}; | ||
} | ||
let val = Reflect.convert(42, NumberType); | ||
console.log(val); // -> 42 | ||
let person = new Person(); | ||
val = Reflect.convert(null, NumberType); | ||
console.log(val); // -> null | ||
person.name = "Thomas"; | ||
console.log(person.name); // -> "Thomas" | ||
val = Reflect.convert(undefined, NumberType); | ||
console.log(val); // -> undefined | ||
Reflect.convert("hello world", NumberType); // throws! | ||
person.name = null; // throws! | ||
``` | ||
@codepen | ||
### type.convert | ||
### Convert a value to a type | ||
Use [can-type/convert] to define a [can-type.typeobject] which will *coerce* the value to the provided type. | ||
Using [can-type/convert] you can define a property that will *convert* any value to that type. | ||
In the following example we have a text input, which will always have a string value, bound to a property of type `type.maybe(Number)`. This converts that value to a [Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number): | ||
```js | ||
import { Reflect, type } from "can"; | ||
import { StacheElement, type } from "can"; | ||
const NumberType = type.convert(Number); | ||
class PersonForm extends StacheElement { | ||
static view = ` | ||
<input type="text" value:to="this.age"> | ||
`; | ||
let val = Reflect.convert("42", NumberType); | ||
console.log(val); // -> 42 | ||
static props = { | ||
age: type.convert(Number) | ||
}; | ||
} | ||
customElements.define("person-form", PersonForm); | ||
let el = new PersonForm(); | ||
document.body.append(el); | ||
el.listenTo("age", () => { | ||
console.log("Age is now", el.age); | ||
}); | ||
``` | ||
@codepen | ||
### type.maybeConvert | ||
Use [can-type/maybeConvert] to define a [can-type.typeobject] which will *coerce* the value to the type, but also accept values of `null` and `undefined`. | ||
### Allowing null/undefined as values | ||
```js | ||
import { Reflect, type } from "can"; | ||
Using [can-type/maybe] you can define types that also allow null/undefined. This is useful when dealing with data sources, such as APIs, where a value might be optional. | ||
const DateType = type.maybeConvert(Date); | ||
This type is *strict* in that any value with a type other than the provided type, `null`, or `undefined` will result in it throwing. | ||
const date = new Date(); | ||
```js | ||
import { ObservableObject, type } from "can"; | ||
let val = Reflect.convert(date, DateType); | ||
console.log(val); // -> date | ||
class Person extends ObservableObject { | ||
static props = { | ||
first: type.check(String), | ||
last: type.maybe(String) | ||
}; | ||
} | ||
val = Reflect.convert(null, DateType); | ||
console.log(val); // -> null | ||
let person = new Person({ | ||
first: "Ted", | ||
last: null | ||
}); | ||
val = Reflect.convert(undefined, DateType); | ||
console.log(val); // -> undefined | ||
val = Reflect.convert("12/04/1433", DateType); | ||
console.log(val); // -> Date{12/04/1433} | ||
console.log(person.first, person.last); // -> "Ted" null | ||
``` | ||
@codepen | ||
### type.check | ||
### Converting a value to a type if not null/undefined | ||
Use [can-type/check] to specify a strongly typed [can-type.typeobject] that verifies the value passed is of the same type. | ||
[can-type/maybeConvert] is a combination of [can-type/maybe] and [can-type/convert]. It will allow `null` or `undefined` as values, but otherwise will try to *convert* the value to the correct type. | ||
```js | ||
import { Reflect, type } from "can"; | ||
import { ObservableObject, type } from "can"; | ||
const StringType = type.check(String); | ||
class ContactInfo extends ObservableObject { | ||
static props = { | ||
phoneNumber: type.maybeConvert(String) | ||
}; | ||
} | ||
let val = Reflect.convert("hello world", StringType); | ||
console.log(val); // -> hello world | ||
let info = new ContactInfo({ | ||
phoneNumber: undefined | ||
}); | ||
Reflect.convert(42, StringType); // throws! | ||
console.log(info.phoneNumber); // -> undefined | ||
info.phoneNumber = 5553335555; | ||
console.log(info.phoneNumber); // -> "5553335555" | ||
``` | ||
@codepen | ||
## Creating Models and ViewModels | ||
### Creating Models and Components | ||
can-type is useful for creating typed properties in [can-observable-object]. You might want to use stricter type checking for some properties or classes and looser type checking for others. The following creates properties with various properties and type methods: | ||
can-type is useful for creating typed properties in [can-observable-object] and [can-stache-element]. | ||
You might want to use stricter type checking for some properties or classes and looser type checking for others. The following creates properties with various properties and type methods: | ||
```js | ||
@@ -100,0 +152,0 @@ import { ObservableObject, type } from "can"; |
@@ -9,9 +9,20 @@ @function can-type/check check | ||
The following creates an object with a strongly typed [Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number) so that any other value cannot be passed to it. | ||
```js | ||
import { Reflect, type } from "can/everything"; | ||
import { ObservableObject, type } from "can"; | ||
let val = Reflect.convert(new Date(), type.check(Date)); | ||
console.log(val); // Date() | ||
class Pagination extends ObservableObject { | ||
static props = { | ||
num: type.check(Number) | ||
}; | ||
} | ||
Reflect.convert("12/14/1933", type.check(Date)); | ||
let page = new Pagination({ num: 1 }); | ||
console.log(page.num); // -> 1 | ||
page.num = 2; | ||
console.log(page.num); // -> 2 | ||
page.num = "4"; | ||
// throws for providing an invalid type. | ||
@@ -21,4 +32,12 @@ ``` | ||
@param {Function} Type A constructor function that values will be checked against. | ||
@param {Function} Type A constructor function that values will be checked against. Often this will be the primitive constructors [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String), [Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number), [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean), or [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date), but could be any class type. | ||
@return {can-type.typeobject} A [can-type.typeobject] which will strictly enforce values of the provided type. | ||
@body | ||
## Use Case | ||
Use __type.check__ to create strongly typed properties. This is useful when used with [can-stache-element StacheElement] components. Strongly typed properties helps to *ensure* that the component works correctly because invalid types cannot creep in and cause unexpected behavior. | ||
Using strongly typed properties with can-type sends a singal to users of the component that the component needs these properties in a certain type. |
@function can-type/convert convert | ||
@parent can-type/methods 2 | ||
@description Create a coercing [can-type.typeobject]. | ||
@description Create a [can-type.typeobject] that converts values to the given type. | ||
@@ -10,10 +10,18 @@ @signature `type.convert(Type)` | ||
```js | ||
import { Reflect, type } from "can/everything"; | ||
import { ObservableObject, type } from "can"; | ||
let date = new Date(); | ||
let val = Reflect.convert(date, type.convert(Date)); | ||
console.log(val); // date | ||
class Event extends ObservableObject { | ||
static props = { | ||
date: type.convert(Date) | ||
}; | ||
} | ||
val = Reflect.convert("12/14/1933", type.convert(Date)); | ||
console.log(val); // Date{12/14/1933} | ||
let event = new Event({ | ||
date: new Date() | ||
}); | ||
console.log(event.date); // -> Date | ||
event.date = "12/14/1933"; | ||
console.log(event.date); // -> Date{12/14/1933} | ||
``` | ||
@@ -25,1 +33,7 @@ @codepen | ||
@return {can-type.typeobject} A [can-type.typeobject] which will *coerce* values to the provided type. | ||
@body | ||
## Use Case | ||
Use __type.convert__ to build types for models, such as those with [can-rest-model] and [can-realtime-rest-model]. You often want to use convert because the database where data is stored will serialize types. This is true of of the [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) type which is often serialized as strings. It's also true of subtypes (like other [can-observable-object ObservableObjects]) which are serialized as plain objects. |
@@ -10,3 +10,3 @@ @function can-type/convertAll convertAll | ||
```js | ||
import { ObservableObject, Reflect, type } from "can/everything"; | ||
import { ObservableObject, Reflect, type } from "can"; | ||
@@ -48,3 +48,3 @@ class Person extends ObservableObject { | ||
```js | ||
import { fixture, ObservableObject, type } from "can/everything"; | ||
import { fixture, ObservableObject, type } from "can"; | ||
@@ -51,0 +51,0 @@ class Person extends ObservableObject { |
@@ -12,3 +12,3 @@ @function can-type/isTypeObject isTypeObject | ||
```js | ||
import { ObservableObject, type } from "can/everything"; | ||
import { ObservableObject, type } from "can"; | ||
@@ -15,0 +15,0 @@ class Faves extends ObservableObject {} |
@@ -9,3 +9,3 @@ @function can-type/late late | ||
```js | ||
import { ObservableObject, type } from "can/everything"; | ||
import { ObservableObject, type } from "can"; | ||
@@ -12,0 +12,0 @@ class Faves extends ObservableObject { |
@@ -10,15 +10,17 @@ @function can-type/maybe maybe | ||
```js | ||
import { Reflect, type } from "can/everything"; | ||
import { ObservableObject, type } from "can"; | ||
let val = Reflect.convert(42, type.maybe(Number)); | ||
console.log(val); // -> 42 | ||
class Person extends ObservableObject { | ||
static props = { | ||
first: type.check(String), | ||
last: type.maybe(String) | ||
}; | ||
} | ||
val = Reflect.convert(null, type.maybe(Number)); | ||
console.log(val); // -> null | ||
let person = new Person({ | ||
first: "Matthew", | ||
last: null | ||
}); | ||
val = Reflect.convert(undefined, type.maybe(Number)); | ||
console.log(val); // -> undefined | ||
Reflect.convert("42", type.maybe(Number)); | ||
// throws for providing an invalid type. | ||
console.log(person.first, person.last); // "Matthew" null | ||
``` | ||
@@ -30,1 +32,44 @@ @codepen | ||
@return {can-type.typeobject} A [can-type.typeobject] which will enforce conversion to the given type. | ||
@body | ||
# Use Case | ||
Using __type.maybe__ you can create types that accept a type, null, or undefined. This is useful in cases where a type is *optional* but it is more convenient to pass a value even when one doesn't exist. | ||
An example is when binding to a child component (such as a [can-stache-element StacheElement]). Using bindings means you'll always pass *something* to that child component, you can't pass nothing. Without a good default value you might want to pass undefined. | ||
```js | ||
import { StacheElement, type } from "can"; | ||
class Child extends StacheElement { | ||
static view = ` | ||
{{# if(this.name) }} | ||
{{name}} | ||
{{ else }} | ||
No name given! | ||
{{/ if }} | ||
`; | ||
static props = { | ||
name: type.maybe(String) | ||
}; | ||
} | ||
customElements.define("child-el", Child); | ||
class Parent extends StacheElement { | ||
static view = ` | ||
<child-el name:from="this.name" /> | ||
`; | ||
static props = { | ||
name: type.maybe(String) | ||
}; | ||
} | ||
customElements.define("parent-el", Parent); | ||
let el = new Parent(); | ||
document.body.append(el); | ||
``` | ||
@codepen |
@@ -10,15 +10,18 @@ @function can-type/maybeConvert maybeConvert | ||
```js | ||
import { Reflect, type } from "can/everything"; | ||
import { ObservableObject, type } from "can"; | ||
let val = Reflect.convert(42, type.maybeConvert(Number)); | ||
console.log(val); // -> 42 | ||
class Person extends ObservableObject { | ||
static props = { | ||
age: maybeConvert(Number) | ||
}; | ||
} | ||
val = Reflect.convert(null, type.maybeConvert(Number)); | ||
console.log(val); // -> null | ||
let person = new Person(); | ||
person.age = 42; // -> 42 | ||
val = Reflect.convert(undefined, type.maybeConvert(Number)); | ||
console.log(val); // -> undefined | ||
person.age = null; // -> null | ||
val = Reflect.convert("42", type.maybeConvert(Number)); | ||
console.log(val); // -> 42 | ||
person.age = undefined; // -> undefined | ||
person.age = "42"; // -> 42 | ||
``` | ||
@@ -30,1 +33,7 @@ @codepen | ||
@return {can-type.typeobject} A [can-type.typeobject] which will enforce conversion to the given type. | ||
@body | ||
## Use Case | ||
Like with [can-type/convert], __type.maybeConvert__ is particularly useful when building models for service layers (like with [can-rest-model] and [can-realtime-rest-model]. Some server-side data layers will transfer empty values in database as either `null` or `undefined`. Using type.maybeConvert will prevent these values from throwing, but also attempt to convert them when a value does exist. |
@@ -9,3 +9,3 @@ @function can-type/normalize normalize | ||
```js | ||
import { type } from "can/everything"; | ||
import { type } from "can"; | ||
@@ -12,0 +12,0 @@ const normalizedType = type.normalize(Date); |
@typedef {{}} can-type.typeobject TypeObject | ||
@parent can-type/types | ||
@parent can-type/types 2 | ||
@description An object describing how to test membership for and convert to a specified type. | ||
@@ -14,3 +14,3 @@ | ||
```js | ||
import { Reflect } from "can/everything"; | ||
import { Reflect } from "can"; | ||
@@ -17,0 +17,0 @@ const dateType = { |
{ | ||
"name": "can-type", | ||
"version": "1.0.3", | ||
"version": "1.1.0", | ||
"description": "Type definitions", | ||
@@ -5,0 +5,0 @@ "homepage": "https://canjs.com/doc/can-type.html", |
@@ -110,3 +110,3 @@ var canSymbol = require("can-symbol"); | ||
}; | ||
canReflect.setName(Integer, "Integer"); | ||
canReflect.setName(Integer, "CustomInteger"); | ||
@@ -198,2 +198,9 @@ var testCases = [ | ||
maybe: throwsBecauseOfWrongType | ||
}, | ||
{ | ||
Type: type.Integer, value: 33.3, | ||
check: throwsBecauseOfWrongType, | ||
convert: checkNumber(33), | ||
maybeConvert: checkNumber(33), | ||
maybe: throwsBecauseOfWrongType | ||
} | ||
@@ -200,0 +207,0 @@ ]; |
53707
29
704