DoneJS StealJS jQuery++ FuncUnit DocumentJS
6.0.1
5.33.2 4.3.0 3.14.1 2.3.35
  • About
  • Guides
  • API Docs
  • Community
  • Contributing
  • Bitovi
    • Bitovi.com
    • Blog
    • Design
    • Development
    • Training
    • Open Source
    • About
    • Contact Us
  • About
  • Guides
  • API Docs
    • Observables
      • can-bind
      • can-compute
      • can-debug
      • can-deep-observable
      • can-define
      • can-define/list/list
      • can-define/map/map
      • can-define-backup
      • can-define-stream
      • can-define-stream-kefir
      • can-event-queue
      • can-kefir
      • can-list
      • can-map
      • can-map-compat
      • can-map-define
      • can-observable-array
      • can-observable-object
      • can-observation
      • can-observation-recorder
      • can-observe
      • can-simple-map
      • can-simple-observable
      • can-stream
      • can-stream-kefir
      • can-value
    • Views
      • can-attribute-observable
      • can-component
      • can-observable-bindings
      • can-stache
      • can-stache-bindings
      • can-stache-converters
      • can-stache-element
      • can-stache-route-helpers
      • can-view-autorender
      • can-view-callbacks
      • can-view-import
      • can-view-model
      • can-view-parser
      • can-view-scope
      • can-view-target
      • steal-stache
    • Data Modeling
      • can-connect
      • can-connect-feathers
      • can-connect-ndjson
      • can-connect-tag
      • can-define-realtime-rest-model
      • can-define-rest-model
      • can-fixture
      • can-fixture-socket
      • can-local-store
      • can-memory-store
      • can-ndjson-stream
      • can-query-logic
      • can-realtime-rest-model
      • can-rest-model
      • can-set-legacy
      • can-super-model
    • Routing
      • can-deparam
      • can-param
      • can-route
      • can-route-hash
      • can-route-mock
      • can-route-pushstate
    • JS Utilities
      • can-assign
      • can-define-lazy-value
      • can-diff
      • can-globals
      • can-join-uris
      • can-key
      • can-key-tree
      • can-make-map
      • can-parse-uri
      • can-queues
      • can-string
      • can-string-to-any
      • can-zone-storage
    • DOM Utilities
      • can-ajax
      • can-attribute-encoder
      • can-child-nodes
      • can-control
      • can-dom-data
      • can-dom-events
      • can-dom-mutate
      • can-event-dom-enter
      • can-event-dom-radiochange
      • can-fragment
    • Data Validation
      • can-define-validate-validatejs
      • can-type
        • methods
          • check
          • maybe
          • convert
          • maybeConvert
          • late
          • isTypeObject
          • normalize
          • all
          • convertAll
        • types
          • Any
          • TypeObject
      • can-validate
      • can-validate-interface
      • can-validate-legacy
      • can-validate-validatejs
    • Typed Data
      • can-cid
      • can-construct
      • can-construct-super
      • can-data-types
      • can-namespace
      • can-reflect
      • can-reflect-dependencies
      • can-reflect-promise
      • can-types
    • Polyfills
      • can-symbol
      • can-vdom
    • Core
    • Infrastructure
      • can-global
      • can-test-helpers
    • Ecosystem
    • Legacy
  • Community
  • Contributing
  • GitHub
  • Twitter
  • Chat
  • Forum
  • News
Bitovi

can-type

  • npm package badge
  • Star
  • Edit on GitHub

Object

Overview

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.

can-type specifies the following type functions:

type.maybe

Use maybe to specify a TypeObject which will accept a value that is a member of the provided type, or is undefined or null.

import { Reflect, type } from "can";

const NumberType = type.maybe(Number);

let val = Reflect.convert(42, NumberType);
console.log(val); // -> 42

val = Reflect.convert(null, NumberType);
console.log(val); // -> null

val = Reflect.convert(undefined, NumberType);
console.log(val); // -> undefined

Reflect.convert("hello world", NumberType); // throws!

type.convert

Use convert to define a TypeObject which will coerce the value to the provided type.

import { Reflect, type } from "can";

const NumberType = type.convert(Number);

let val = Reflect.convert("42", NumberType);
console.log(val); // -> 42

type.maybeConvert

Use maybeConvert to define a TypeObject which will coerce the value to the type, but also accept values of null and undefined.

import { Reflect, type } from "can";

const DateType = type.maybeConvert(Date);

const date = new Date();

let val = Reflect.convert(date, DateType);
console.log(val); // -> date

val = Reflect.convert(null, DateType);
console.log(val); // -> null

val = Reflect.convert(undefined, DateType);
console.log(val); // -> undefined

val = Reflect.convert("12/04/1433", DateType);
console.log(val); // -> Date{12/04/1433}

type.check

Use check to specify a strongly typed TypeObject that verifies the value passed is of the same type.

import { Reflect, type } from "can";

const StringType = type.check(String);

let val = Reflect.convert("hello world", StringType);
console.log(val); // -> hello world

Reflect.convert(42, StringType); // throws!

Creating Models and ViewModels

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:

import { ObservableObject, type } from "can";

class Person extends ObservableObject {
  static props = {
    first: type.check(String), // type checking is the default behavior
    last: type.maybe(String),

    age: type.convert(Number),
    birthday: type.maybeConvert(Date)
  };
}

let fib = new Person({
  first: "Fibonacci",
  last: null,
  age: "80",
  birthday: undefined
});

console.log(fib); // ->Person{ ... }

Note: as mentioned in the comment above, type checking is the default behavior of can-observable-object, so first: type.check(String) could be written as first: String.

When creating models with can-rest-model you might want to be loose in the typing of properties, especially when working with external services you do not have control over.

On the other hand, when creating ViewModels for components, such as with can-stache-element you might want to be stricter about how properties are passed, to prevent mistakes.

import { StacheElement, type } from "can";

class Progress extends StacheElement {
  static props = {
    value: {
      type: type.check(Number),
      default: 0
    },
    max: {
      type: type.check(Number),
      default: 100
    },
    get width() {
      let w = (this.value / this.max) * 100;
      return w + '%';
    }
  };

  static view = `
    <div style="background: black;">
      <span style="background: salmon; display: inline-block; width: {{width}}">&nbsp;</span>
    </div>
  `;
}

customElements.define("custom-progress-bar", Progress);

let progress = new Progress();
progress.value = 34;

document.body.append(progress);

function increment() {
  setTimeout(() => {
    if(progress.value < 100) {
      progress.value++;
      increment();
    }
  }, 500);
}

increment();

Note: Having both type: type.check(Number) and default: 0 in the same definition is redundant. Using default: 0 will automatically set up type checking. It is shown above for clarity.

See can-stache-element and can-observable-object for more on these APIs.

How it works

The can-type methods work by creating functions that are compatible with canReflect.convert.

These functions have a can.new Symbol that points to a function that is responsible for creating an instance of the type. The following is an overview of how this function works:

1. Determine if value is already the correct type

  • Maybe types (type.maybe, type.maybeConvert) will return true if the value is null or undefined.
  • Common primitive types (Number, String, Boolean) will return true if typeof returns the correct result.
  • Other types will return true if the value is an instanceof the type.
  • TypeObjects (or anything with a can.isMember Symbol) will return true if the can.isMember function returns true.
  • Otherwise, the value is not the correct type.

2. Handle values of another type

If the value is not the correct type:

  • type.maybe and type.check will throw an error.
  • type.convert and type.maybeConvert will convert the value using convert.

Applying multiple type functions

The type functions check, convert, maybe, and maybeConvert all return a TypeObject. Since they also can take a TypeObject as an argument, this means you can apply multiple type functions.

For example, using convert and maybe is equivalent to using maybeConvert:

import { Reflect, type } from "can";

const MaybeConvertString = type.convert(type.maybe(String));

console.log(2, Reflect.convert(2, MaybeConvertString)); // "2"
console.log(null, Reflect.convert(2, MaybeConvertString)); // null

Another example is taking a strict type and making it a converter type by wrapping with convert:

import { Reflect, can } from "can";

const StrictString = type.check(String);
const NonStrictString = type.convert(StrictString);

console.log("Converting: ", Reflect.convert(5, NonStrictString)); // "5"

This works because the type functions all keep a reference to the underlying type and simply toggle the strictness of the newly created TypeObject. When can.new is called the strictness is checked.

CanJS is part of DoneJS. Created and maintained by the core DoneJS team and Bitovi. Currently 6.0.1.

On this page

Get help

  • Chat with us
  • File an issue
  • Ask questions
  • Read latest news