I'm struggling with type checking of TypeScript. For example following code:
export function deepClone<T>(obj: T): T { // make sure that deepClone generates the same type as obj
if (obj == null || typeof obj !== 'object') {
return obj;
}
switch (Object.prototype.toString.call(obj)) {
case '[object Array]': {
const result = new Array(obj.length);
for (let i=0; i<result.length; ++i) {
result[i] = deepClone(obj[i]);
}
return result as any as T;
}
// Object.prototype.toString.call(new XxxError) returns '[object Error]'
case '[object Error]': {
const result = new obj.constructor(obj.message);
result.stack = obj.stack; // hack...
return result;
}
case '[object Date]':
case '[object RegExp]':
case '[object Int8Array]':
case '[object Uint8Array]':
case '[object Uint8ClampedArray]':
case '[object Int16Array]':
case '[object Uint16Array]':
case '[object Int32Array]':
case '[object Uint32Array]':
case '[object Float32Array]':
case '[object Float64Array]':
case '[object Map]':
case '[object Set]':
return new obj.constructor(obj);
case '[object Object]': {
const keys = Object.keys(obj);
const result: any = {};
for (let i=0; i<keys.length; ++i) {
const key = keys[i];
result[key] = deepClone(obj[key]);
}
return result;
}
default: {
throw new Error("Unable to copy obj! Its type isn't supported.");
}
}
}
I get errors on const result = new Array(obj.length)
. I know obj's type is any[]
but ts compiler fails to recognize it. I have to write the ugly const tmp = obj as any as any[]
but it results in extra useless code generation, or I have to write obj as any as whatever
in every line that uses obj
Writing function deepClone<T extends any>(obj: T): T
works but it disables most type checking.
Another case:
const el = document.getElementById('sth');
switch (el.tagName) {
case 'INPUT': // Now I know el is a HTMLInputElement element
el.value = '123'; // Error: HTMLElement doesn't contain property 'value'
(el as HTMLInputElement).value = '123'; // works
(el as HTMLInputElement).valueAsNumber = 123; // again
(el as HTMLInputElement).valueAsDate = xxx; // unacceptable