如何在 TypeScript 中创建 TryCast(.Net 等价物)
How to create TryCast in TypeScript ( .Net Equivalent )
我很惊讶我们在 TypeScript Docs 中没有看到太多关于演员表的内容。尝试在 TypeScript 中创建一个简单的 tryCast 函数,类似于我们在 .Net 中的函数。所以基本上,尝试转换为指定的类型,如果失败 return null / nothing。也许应该使用 typescript generic
<'T> 语法,以及如何让它与原始类型之外的对象一起工作?
function tryCast(obj: any, type: string) : any {
if (typeof obj === type) {
switch (type) {
case 'number':
return Number(obj);
case 'string':
return String(obj);
case 'boolean':
return Boolean(obj);
}
}
return null;
}
function testCast() {
var num: number = 123;
var str: string = tryCast(num, 'string');
if (!str) {
console.log('Cast failed');
}
else {
console.log('Cast succeeded');
}
}
这是您要找的吗?
interface IFoo { bar: any; }
function tryCast(obj: number, type: "number"): number;
function tryCast(obj: string, type: "string"): string;
function tryCast(obj: boolean, type: "boolean"): boolean;
function tryCast(obj: IFoo, type: "IFoo"): IFoo;
function tryCast(obj: any, type: string): any;
function tryCast(obj: any, type: string): any {
// Do fancy stuff to cast things
}
// IFoo
tryCast({ bar: 'hi' }, 'IFoo');
// any (NULL)
tryCast({}, 'string');
// boolean
tryCast( !!1 , 'boolean');
// any (NULL)
tryCast(123, 'boolean');
// number
tryCast(123, 'number');
// string
tryCast( 123 + '', 'string');
这是一个有趣的问题。我想不出一个好的场景,这种情况经常发生以保证通用功能。大多数时候,我会根据具体情况进行处理。通常对基元使用 typeof
,对用户定义类型使用 duck typing。然而,这是一个很好的练习,所以我会试一试。
您的代码正在执行 typeof
检查,但随后将变量冗余地传递给构造函数。如果typeof obj === 'number'
那么你已经有号码了,没必要"cast"。这是上面 @mallison 给出的答案的修改版本。我还向该解决方案添加了一个 类,但是接口并不像您希望的那样工作。
// Overloaded function
function tryCast(o: number, t: "number"): number;
function tryCast(o: string, t: "string"): string;
function tryCast(o: boolean, t: "boolean"): boolean;
function tryCast(o: IFace, t: "IFace"): IFace;
function tryCast(o: Function, t: "function"): Function;
function tryCast<T>(o: any, t: string): T;
function tryCast<T>(o: T, t: any): T {
if (t === typeof o || (o['constructor'] && t === o['constructor']['name'])) {
return o;
} else {
return null;
}
}
// Usage examples
var s1 = tryCast(70, 'string');
var s2 = tryCast('hello world', 'string');
var b1 = tryCast({b:true}, 'boolean');
var b2 = tryCast(true, 'boolean');
var n1 = tryCast('nan', 'number');
var n2 = tryCast(91, 'number');
var f1 = tryCast(123, 'function');
var f2 = tryCast(function foo() { return 1}, 'function');
class Classy { public sanDeigo = true; }
var c1 = tryCast<Classy>({soFly:false}, 'Classy');
var c2 = tryCast<Classy>(new Classy(), 'Classy');
interface IFace { eyes: number; hasMouth: boolean; }
var i1 = tryCast<IFace>({ eyes: 2, fake: true }, 'IFace');
var i2 = tryCast<IFace>({ eyes: 2, hasMouth: true }, 'IFace');
// Runtime tests
document.write(`
s1:${s1}, // expect null<br/>
s2:${s2}, // expect string<br/>
b1:${b1}, // expect null<br/>
b2:${b2}, // expect boolean<br/>
n1:${n1}, // expect null<br/>
n2:${n2}, // expect number<br/>
f1:${f1}, // expect null<br/>
f2:${f2}, // expect function<br/>
c1:${c1}, // expect null<br/>
c2:${c2}, // expect Classy<br/>
i1:${i1}, // expect null<br/>
i2:${i2}, // expect IFace...but its not!<br/>
`);
// Compiler tests
s1.toUpperCase();
s2.toUpperCase();
b1.valueOf();
b2.valueOf();
n1.toFixed(2);
n2.toFixed(2);
f1.apply(this);
f2.apply(this);
c1.sanDeigo;
c2.sanDeigo;
i1.eyes;
i2.eyes;
如果你编译 运行 the code 你会看到以下输出:
s1:null, // expect null
s2:hello world, // expect string
b1:null, // expect null
b2:true, // expect boolean
n1:null, // expect null
n2:91, // expect number
f1:null, // expect null
f2:function foo() { return 1; }, // expect function
c1:null, // expect null
c2:[object Object], // expect Classy
i1:null, // expect null
i2:null, // expect IFace...but its not!
所以这是怎么回事?没有转换接口的通用方法,因为它是一个编译时实体。 运行时间不知道接口。 class 之所以有效,是因为我们知道 TypeScript 将如何编译代码,但如果构造函数名称与 class 名称不同,这在将来可能不起作用(您可能想在之前检查 ES6 规范在生产中使用它)。
同样,在 运行 时检查接口类型的唯一方法是鸭子类型,正如我之前提到的。对于在编译时您不知道类型的情况,我将为您提供我认为正确的解决方案。
$.getJSON('data.json', function(data: IFace) {
if (data && data.eyes > 0 && data.hasMouth) {
// this looks like an IFace to me!
} else {
// data is no good!
}
});
我很惊讶我们在 TypeScript Docs 中没有看到太多关于演员表的内容。尝试在 TypeScript 中创建一个简单的 tryCast 函数,类似于我们在 .Net 中的函数。所以基本上,尝试转换为指定的类型,如果失败 return null / nothing。也许应该使用 typescript generic <'T> 语法,以及如何让它与原始类型之外的对象一起工作?
function tryCast(obj: any, type: string) : any {
if (typeof obj === type) {
switch (type) {
case 'number':
return Number(obj);
case 'string':
return String(obj);
case 'boolean':
return Boolean(obj);
}
}
return null;
}
function testCast() {
var num: number = 123;
var str: string = tryCast(num, 'string');
if (!str) {
console.log('Cast failed');
}
else {
console.log('Cast succeeded');
}
}
这是您要找的吗?
interface IFoo { bar: any; }
function tryCast(obj: number, type: "number"): number;
function tryCast(obj: string, type: "string"): string;
function tryCast(obj: boolean, type: "boolean"): boolean;
function tryCast(obj: IFoo, type: "IFoo"): IFoo;
function tryCast(obj: any, type: string): any;
function tryCast(obj: any, type: string): any {
// Do fancy stuff to cast things
}
// IFoo
tryCast({ bar: 'hi' }, 'IFoo');
// any (NULL)
tryCast({}, 'string');
// boolean
tryCast( !!1 , 'boolean');
// any (NULL)
tryCast(123, 'boolean');
// number
tryCast(123, 'number');
// string
tryCast( 123 + '', 'string');
这是一个有趣的问题。我想不出一个好的场景,这种情况经常发生以保证通用功能。大多数时候,我会根据具体情况进行处理。通常对基元使用 typeof
,对用户定义类型使用 duck typing。然而,这是一个很好的练习,所以我会试一试。
您的代码正在执行 typeof
检查,但随后将变量冗余地传递给构造函数。如果typeof obj === 'number'
那么你已经有号码了,没必要"cast"。这是上面 @mallison 给出的答案的修改版本。我还向该解决方案添加了一个 类,但是接口并不像您希望的那样工作。
// Overloaded function
function tryCast(o: number, t: "number"): number;
function tryCast(o: string, t: "string"): string;
function tryCast(o: boolean, t: "boolean"): boolean;
function tryCast(o: IFace, t: "IFace"): IFace;
function tryCast(o: Function, t: "function"): Function;
function tryCast<T>(o: any, t: string): T;
function tryCast<T>(o: T, t: any): T {
if (t === typeof o || (o['constructor'] && t === o['constructor']['name'])) {
return o;
} else {
return null;
}
}
// Usage examples
var s1 = tryCast(70, 'string');
var s2 = tryCast('hello world', 'string');
var b1 = tryCast({b:true}, 'boolean');
var b2 = tryCast(true, 'boolean');
var n1 = tryCast('nan', 'number');
var n2 = tryCast(91, 'number');
var f1 = tryCast(123, 'function');
var f2 = tryCast(function foo() { return 1}, 'function');
class Classy { public sanDeigo = true; }
var c1 = tryCast<Classy>({soFly:false}, 'Classy');
var c2 = tryCast<Classy>(new Classy(), 'Classy');
interface IFace { eyes: number; hasMouth: boolean; }
var i1 = tryCast<IFace>({ eyes: 2, fake: true }, 'IFace');
var i2 = tryCast<IFace>({ eyes: 2, hasMouth: true }, 'IFace');
// Runtime tests
document.write(`
s1:${s1}, // expect null<br/>
s2:${s2}, // expect string<br/>
b1:${b1}, // expect null<br/>
b2:${b2}, // expect boolean<br/>
n1:${n1}, // expect null<br/>
n2:${n2}, // expect number<br/>
f1:${f1}, // expect null<br/>
f2:${f2}, // expect function<br/>
c1:${c1}, // expect null<br/>
c2:${c2}, // expect Classy<br/>
i1:${i1}, // expect null<br/>
i2:${i2}, // expect IFace...but its not!<br/>
`);
// Compiler tests
s1.toUpperCase();
s2.toUpperCase();
b1.valueOf();
b2.valueOf();
n1.toFixed(2);
n2.toFixed(2);
f1.apply(this);
f2.apply(this);
c1.sanDeigo;
c2.sanDeigo;
i1.eyes;
i2.eyes;
如果你编译 运行 the code 你会看到以下输出:
s1:null, // expect null
s2:hello world, // expect string
b1:null, // expect null
b2:true, // expect boolean
n1:null, // expect null
n2:91, // expect number
f1:null, // expect null
f2:function foo() { return 1; }, // expect function
c1:null, // expect null
c2:[object Object], // expect Classy
i1:null, // expect null
i2:null, // expect IFace...but its not!
所以这是怎么回事?没有转换接口的通用方法,因为它是一个编译时实体。 运行时间不知道接口。 class 之所以有效,是因为我们知道 TypeScript 将如何编译代码,但如果构造函数名称与 class 名称不同,这在将来可能不起作用(您可能想在之前检查 ES6 规范在生产中使用它)。
同样,在 运行 时检查接口类型的唯一方法是鸭子类型,正如我之前提到的。对于在编译时您不知道类型的情况,我将为您提供我认为正确的解决方案。
$.getJSON('data.json', function(data: IFace) {
if (data && data.eyes > 0 && data.hasMouth) {
// this looks like an IFace to me!
} else {
// data is no good!
}
});