@rustable/type
Version:
A TypeScript implementation of Rust-like type system with generic support and runtime type information.
138 lines (136 loc) • 3.67 kB
JavaScript
;
const typeMap = /* @__PURE__ */ new WeakMap();
const typeIdMap = /* @__PURE__ */ new WeakMap();
let i = 0;
function typeId(target, genericParams) {
if (target === null || target === void 0) {
throw new Error("Cannot get typeId of null or undefined");
}
const constructor = Type(type(target), genericParams);
const existingId = typeIdMap.get(constructor);
if (existingId !== void 0) {
return existingId;
}
const id = `${++i}:${constructor.name}`;
typeIdMap.set(constructor, id);
return id;
}
const genericType = Symbol("GenericType");
function getGenericKey(genericParams) {
return genericParams.map((param) => {
return typeId(param);
}).join(",");
}
function getGenericName(genericParams) {
return genericParams.map((param) => {
return param.name;
}).join(",");
}
function Type(target, genericParams, newWithTypes = false) {
validNull(target);
if (genericParams && !Array.isArray(genericParams)) {
throw new Error("Generic parameters must be an array");
}
if (target[genericType]) {
if (!genericParams || genericParams.length === 0) {
return target;
} else {
throw new Error("Cannot specify generic parameters for generic type");
}
}
if (!genericParams || genericParams.length === 0) {
return target.prototype.constructor;
}
const targetConstructor = target.prototype.constructor;
let targetTypes = typeMap.get(targetConstructor);
if (!targetTypes) {
targetTypes = /* @__PURE__ */ new Map();
typeMap.set(targetConstructor, targetTypes);
}
const genericKey = getGenericKey(genericParams);
let customType = targetTypes.get(genericKey);
if (!customType) {
customType = class extends targetConstructor {
constructor(...args) {
if (newWithTypes) {
super(genericParams, ...args);
} else {
super(...args);
}
}
};
Object.getOwnPropertyNames(targetConstructor).forEach((prop) => {
if (prop !== "prototype" && prop !== "name") {
Object.defineProperty(customType, prop, Object.getOwnPropertyDescriptor(targetConstructor, prop));
}
});
const config = {
writable: false,
enumerable: false,
configurable: false
};
Object.defineProperties(customType, {
name: {
value: `${targetConstructor.name}<${getGenericName(genericParams)}>`,
...config
},
[genericType]: {
value: true,
...config
},
generics: {
value: genericParams,
...config
}
});
targetTypes.set(genericKey, customType);
}
return customType;
}
function isGenericType(target) {
validNull(target);
return target[genericType] === true;
}
function getGenerics(target) {
if (!isGenericType(target)) {
return [];
}
return target.generics || [];
}
function typeName(target) {
validNull(target);
if (typeof target === "function") {
return target.name;
} else {
return target.constructor.name;
}
}
function type(target) {
validNull(target);
if (typeof target === "function") {
return target;
} else {
return target.constructor;
}
}
function validNull(target) {
if (target === null) {
throw new Error("Cannot get type of null");
}
if (target === void 0) {
throw new Error("Cannot get type of undefined");
}
}
function named(name, target) {
const n = function(t) {
Object.defineProperty(t, "name", {
value: name,
writable: false,
enumerable: false,
configurable: true
});
return t;
};
return target ? n(target) : n;
}
export { Type, getGenerics, isGenericType, named, type, typeId, typeName };