get(obj, key) {
		if (Object.prototype.hasOwnProperty.call(obj, key)) {
			return obj[key];
		}

		if (typeof key !== 'string') return;

		if (key.startsWith('is')) {
			// Handle { type: 'StringLiteral', value: any }
			let type = key.substring(2);
			// Handle { type:'Literal', value:'string' }
			let alt, typeCheck, m;
			if ((m = type.toLowerCase().match(/^string|num|boolean/g))) {
				alt = 'Literal';
				typeCheck = m[0].replace('num', 'number');
			}
			obj[key] = pathOrNode => {
				if (pathOrNode == null) return false;
				const node = pathOrNode instanceof Path ? pathOrNode.node : pathOrNode;
				if (node.type === type) return true;
				if (node.type === alt && typeof node.value === typeCheck) return true;
				return false;
			};
			return obj[key];
		}

		const type = key[0].toUpperCase() + key.substring(1);
		// @TODO this doesn't cover all cases well enough.
		// Ideally it would be nice to avoid inlined defs here.
		const prop = /Literal/.test(key) ? 'value' : 'expression';
		obj[key] = v => ({ type, [prop]: v });
		return obj[key];
	}
@type {typeof TYPES & Record<string, (...any: any[]) => Partial<Node>> & Record<string, (expr: Path | Node) => boolean>}