假设我有一个函数changeType
,它接受一个Foo
并将其转换为一个Bar
。
interface Foo {
num: number;
}
interface Bar {
str: string;
}
function changeType(foo: Foo): void {
(foo as any).str = foo.num.toString();
delete (foo as any).num;
}
let foobar: Foo | Bar = { num: 1 };
changeType(foobar);
是否可以告诉TypeScript编译器运行changeType
一定会将传入的参数更改为其他类型?我以为可能会是这样的:
function changeType(foo: Foo): foo as Bar { /*...*/ }
,然后该函数将必须返回foo
而不是不返回任何内容。甚至更好的是一些奇怪的语法,例如function(...): void, foo => Bar { ... }
。
我知道这是一个非常奇怪的情况,但这是处理Object.freeze
的一部分-在讨论如何实现时似乎被忽略了。通常,the conversation专注于返回的值,而不关注修改后的传递值本身。
另一种情况是关于继承的。假设您有一个Triangle
和一个Parallelogram
都继承自Shape
。
type Point = [number, number];
interface Shape {
name: 'triangle' | 'square';
points: Point[];
}
interface Triangle extends Shape {
name: 'triangle';
points: [Point, Point, Point];
}
interface Parallelogram extends Shape {
name: 'parallelogram';
points: [Point, Point, Point, Point];
}
现在,您想将Triangle
变成一个Parallelogram
,而不是创建一个新的Shape
对象。
function makeParallelogram(triangle: Triangle): void;
function makeParallelogram(shape: Shape): void {
Object.assign(shape, {
name: 'parallelogram',
points: (shape as Triangle).points.push(calcFourthPoint(shape as Triangle);
});
}
function calcFourthPoint(triangle: Triangle): Point {
/* some geometry here */
}
答案 0 :(得分:2)
使用TypeScript不可能实现您真正想要的。来自GitHub的issue非常接近您想要的。 TypeScript无法深入分析代码的控制流,因此很难从函数的作用中推断出正确的类型。
但是,如果使用return语句,重载和type guards,则可以获得相同的效果。
function isFoo(f: Foo | Bar): f is Foo {
return (f as Foo).num !== undefined;
}
function changeType(foo: Foo): Bar
function changeType(foo: Bar): Foo
function changeType(foo: Foo | Bar): Foo | Bar {
if (isFoo(foo)) {
// Convertion logic can be anything. But I suggest not to mutate original object but create new one and fill it with props.
let res: Bar = { str: foo.num.toString() }
return res;
}
else {
let res: Foo = { num: +foo.str }
return res;
}
}
以及用法
let foobar: Foo | Bar = { num: 1 }; // Type is Foo
let r: Bar = changeType(foobar); // Return is Bar