Это интересный вопрос. Я не могу придумать хороший сценарий, когда это происходит достаточно часто, чтобы гарантировать общую функцию. В большинстве случаев я бы рассматривал его в каждом конкретном случае. Обычно используют typeof
для примитивов и duck typing для пользовательских типов. Однако это хорошее упражнение, поэтому я дам ему выстрел.
Ваш код выполняет проверку typeof
, но затем он избыточно передает переменную в конструктор. Если typeof obj === 'number'
, то у вас уже есть номер, не нужно «бросать». Ниже приведена измененная версия ответа, приведенная @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!
Так что же происходит? Не существует общего способа создания интерфейса, поскольку он является объектом компиляции. Время выполнения не знает о интерфейсах. Класс работает, потому что мы знаем, как TypeScript будет компилировать код, но это может не работать в будущем, если имя конструктора не совпадает с именем класса (вы можете проверить спецификацию 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!
}
});
Как это может быть как машинопись 1.4, так и машинопись 1.5? Выберите * один * тег. – royhowie
@royhowie Хорошо все сделано. – wayofthefuture