我正在努力进行 TypeScript 的类型检查。例如下面的代码:
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.");
}
}
}
我在const result = new Array(obj.length)
. 我知道 obj 的类型是,any[]
但 ts 编译器无法识别它。我必须编写丑陋的const tmp = obj as any as any[]
代码,但它会导致额外无用的代码生成,或者我必须obj as any as whatever
在使用的每一行中编写obj
写入function deepClone<T extends any>(obj: T): T
有效,但它禁用了大多数类型检查。
另一个案例:
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
如果你想更接近无断言的代码,你将需要使用类型保护。有几种可能的类型保护,但打开的结果toString
不是其中之一。使用instanceof
和自定义类型保护的版本可能是:
export function deepClone<T
extends
number | string | boolean |
Array<any> |
Error |
Date | RegExp |
Int8Array | Uint8Array | Uint8ClampedArray |
Int16Array | Uint16Array |
Int32Array | Uint32Array |
Float32Array | Float64Array | Map<any, any> | Set<any>>(obj: T): T { // make sure that deepClone generates the same type as obj
if (obj == null || typeof obj !== 'object') {
return obj;
}
if (obj instanceof 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;
}
else if (obj instanceof Error) {
const constructor = obj.constructor as new (p: any) => T & Error;
const result = new constructor(obj.message);
result.stack = obj.stack;
return result;
}
else if (obj instanceof Date || obj instanceof RegExp ||
obj instanceof Int8Array || obj instanceof Uint8Array || obj instanceof Uint8ClampedArray ||
obj instanceof Int16Array || obj instanceof Uint16Array ||
obj instanceof Int32Array || obj instanceof Uint32Array ||
obj instanceof Float32Array || obj instanceof Float64Array || obj instanceof Map || obj instanceof Set) {
const constructor = obj.constructor as new (p: T) => T;
return new constructor(obj);
}
else if (isObject(obj)) {
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;
} else {
throw new Error("Unable to copy obj! Its type isn't supported.");
}
function isObject(obj: object | T): obj is { [k: string]: any } {
return typeof obj === 'object'
}
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句