Assertions: value, type and instance

We are all familiar with python and nodejs assertions:

// nodejs

assert(1+1 === 2)
// true but on failure throws AssertionError

# python

assert 1+1 == 2
# same as above

This idea expands upon the above concept and looks at assertions in extra detail along with a cooler syntax😉

warning

A lot of unstable terrain ahead! proceed with caution.

Value Assertions

These are the normal assertions that we are all familiar from node and python.

1 + 1 :: 2 // true
"ecmascript" :: "javascript" // AssertionError
"string" :: typeof "hi" // true

The last example is particularly interesting.
notice that a value assertion cannot begin with a typeof operator; because that's a whole other kind of assertion👇

Type Assertions

These provide a way for type verification.
Notice however they follow a particular syntax and remember that they are not meant to provide solid static typing.

let x = "JavaScript"
typeof x :: String

let y = String
typeof y :: Object

let z = [1, 2, 3, 4, 5, 6, 7]
typeof z :: [Array(7) Number]

let a = {
	foo: "bar",
	[Symbol("sim")]: 100
}
typeof a :: Object
// or [Object { String: String, Symbol: Number }]
// irrespective of order

typeof null :: null // I don't know, maybe it should be [Object null]

typeof undefined :: undefined

// some other exotic Objects

let b = new Set(1, 2, 3, 4, 5, 6, 7)
typeof b :: [Set(7) Number]


let c = new Map([ [0, 1], [2, 3], [4, 5], [6, 7] ])
typeof b :: [Map {Number : Number}]
// or [Map Number] -- typeof keys only
// or [Map {:Number} ] -- typeof values only
// or [Map {Number} ] -- typeof keys and values

// Functions
typeof greet :: [Function AsyncFunction]
async function greet(word) {
	typeof word :: String
	const greetings = word + " world!"
	typeof greetings :: String
	return greetings
}

// generators
typeof speak :: [Function AsyncGenerator]
// if it's simply a generator then [Function Generator]
async function *speak(firstWord) {
	typeof firstWord :: String
	yield firstWord
	
	const word2 = "Buzz"
	typeof word2 :: String
	yield word2
	
	const word3 = "Zap"
	typeof word3 :: String
	yield word3
	
	const lastWord = "Bye"
	typeof lastWord :: String
	return lastWord
}

class Animate {
	x2 = 0
	y2 = 0
	constructor(x, y) {
		this.x1 = x
		this.y1 = y
		
		typeof this.x1 :: Number
		typeof this.y1 :: Number
		
		typeof this.x2 :: Number
		typeof this.y2 :: Number
		
		typeof this.moveTo :: Function
		// this is trivial any away
	}
	
	moveTo(x2, y2) {
		typeof x2 :: Number
		typeof y2 :: Number
		// do something
		typeof [x2, y2] :: [Array Number]
		return [x2, y2]
	}
}
typeof Animate :: [Function Object]

We can take this further and create algebraic types

let str = "I'm  a String"
typeof str :: String | Number

let sym = Symbol("I'm Invincible")
typeof sym :: !String
// sym can be anything except a String

typeof sym :: !(String | Number)
// means that sym can be anything except a String and a Number

The real power of algebraic types can be seen from Arrays and other collection types.

typeof [1, "hi", 3, true] :: [Array Number | String | Boolean]

typeof new Map([ ["a", "apple"], [0, false] ]) :: [Map {Number | String: Number | Boolean}]

Instance Assertions

These assertions are for instance checking

instanceof Error :: TypeError // true
instanceof Human :: Person | Mammal // true
instanceof Insect :: Dodo // AssertionError

I personally think they are cool and would make JavaScript even cooler!
What's your take on this idea??

This is a lengthy idea, so there might be some errors, misunderstandings and syntax ambiguities along the way! please care to mention them and help refine this idea!

2 Likes

You may be interested in some of the syntax ideas in GitHub - tc39/proposal-pattern-matching: Pattern matching syntax for ECMAScript .

match (typeof value) {
  when ('object' | 'function') if (value !== null) { … }
  when ('string' | 'number') { … }
  else { … }
}

Maybe this issue: Builtin matchers for primitives (string | number | boolean | bigint | nullish) · Issue #209 · tc39/proposal-pattern-matching · GitHub too

1 Like

One problem with these types of assertions:

typeof myArray :: [Array Number]
typeof myMap :: [Map {Number | String: Number | Boolean}]

is there's no efficient way to implement this. You would have to visit every element to check if they are all the correct type.

yes that's very true. It's not meant to be a static typing mechanism. Its purpose is to ensure relative correctness. Correctness costs performance; that's an obvious thing in dynamic languages(I'm quoting Python and TS here :smirk:).
This idea will surely help in unit tests ;)
Assertions usually cost something but it's usually worth it!
I'm no expert but I sure believe that the browser vendors might find a way to optimise it; (They usually does, right??)

I guess the engine can at least check this more efficiently than userland code (e.g. ElementKind arrays in V8).

I currently write a lot of code in languages that do have explicit assertion syntax and semantics, and I must say that this is something that I'm missing in JS.

I'm a bit sceptical concerning the proposed syntax I'd rather prefer something like

assert hopefullyArray instanceof Array;
assert typeof hopefullyString === "string";
1 Like

Thanks Jonas;
I considered the new syntax for a couple of reasons.

  • To avoid the undesirable results of typeof & instanceof operators.
  • To give us more expressiveness(through the Object toStringTag syntax)
  • To support alterations and negations(algebraic types)

This doesn't mean in any way that it's perfect or complete. It's not as powerful / coherent as static typing but still does a good job at documentation and improving correctness.

and of course you can do it as:

instanceof Array :: hopefullyArray
// or
true :: hopefullyArray instanceof Array

typeof hopefullyString :: String
// or
"string" :: typeof hopefullyString

This can be further extended (as mentioned earlier by others) through the pattern matching.

Hello @jithujoshyjy,

I like your idea, but I think we can do most (not all) assertions with just JSDocs when @ts-check has been enabled.

JSDocs is an annotation mechanism while this is like console.assert() / node assert(); aka an assertion mechanism. I do like JSDocs. It can help in assertions but it is limited to comments and hence not very interactable through code.
(spoiler: I just wanted a little haskell in JS😉)

1 Like