如果if
的函数调用返回never
类似于undefined
的函数调用,那么这种情况是否可以限制assert
1}}
示例代码:
interface Foo { bar(): void }
function getFoo(): Foo | undefined { }
function test() {
const foo = someService.getFoo();
assert(foo);
if (!foo) { // now mandatory because without this foo may be still undefined even if assert protects us from this
return;
}
foo.bar(); // , here foo may be undefined
}
我希望能够以这样的方式编写assert
,以便我可以跳过if (!foo)
子句并将foo
类型限制为普通Foo
。
这在打字稿中是否可行?
我尝试为never
添加带有抛出的类型的重载:
function assertGuard(v: undefined | null | '' | 0 | false): never;
function assertGuard(v: any): void; // i'm not sure which one is captured by TS typesystem here
function assertGuard<T>(v: T | undefined) {
if (v === undefined || v === null || v === '' || v === 0 || v === false) {
throw new AssertionError({message: 'foo'})
}
}
这个编译,但是对assertGuard(foo)
的调用无法识别undefined
,它会返回never
,因此不会将foo
限制为Foo
我找到了可能的解决方法,但我认为经典的assert
是一种更清洁的方法:
function assertResultDefined<T>(v: T|undefined): T | never {
if (v === undefined) {
throw new Error('foo');
}
return v;
}
function die(): never { throw new Error('value expected)}
const foo = assertResultDefined(getFoo()) // foo is Foo, undefined is erased
const foo = getFoo() || die();
// undefined is erased from foo
/ CONS: doesn't play well with types that interpolate to `false` like 0, ''
答案 0 :(得分:3)
此https://github.com/Microsoft/TypeScript/issues/8655的打字稿积压存在问题。所以现在你不能这样做。
您可以做的是使用“非空断言运算符”。哪个强制打字稿放松非空检查。使用这种情况,你绝对确定它不会导致空引用。
function test() {
const foo = getFoo();
assert(foo);
foo!.bar(); //Dang it typescript! I know better.
}
答案 1 :(得分:3)
Typescript 3.7添加了assertions in control flow analysis。
返回
asserts
的谓词表示该函数仅在断言成立时返回,否则抛出异常
消费者方面不再需要花费金钱。
interface Foo { bar(): void }
declare function getFoo(): Foo | undefined;
function assert(value: unknown): asserts value {
if (value === undefined) {
throw new Error('value must be defined');
}
}
function test() {
const foo = getFoo();
// foo is Foo | undefined here
assert(foo);
// foo narrowed to Foo
foo.bar();
}
答案 2 :(得分:1)
由于foo
为Foo | undefined
,其类型应以某种方式更改为Foo
。
在上面的代码中,这可以合理地完成:
let foo = getFoo(); // Foo | undefined
foo = assertResultDefined(foo); // Foo
foo.bar();
另一种选择是使用非空断言(如另一个答案所示):
let foo = getFoo();
foo = assertResultDefined(foo);
foo = foo!;
foo.bar();
答案 3 :(得分:1)
这应该对您有用:
const foo = (a: number | null) => {
a = shouldBe(_.isNumber, a)
a // TADA! a: number
}
const shouldBe = <T>(fn: (t1) => t1 is T, t) => (fn(t) ? t : throwError(fn, t))
const throwError = (fn:Function, t) => {
throw new Error(`not valid, ${fn.name} failed on ${t}`)
}
其中_.isNumber
具有类型保护x is number
可以与带有类型保护的任何功能一起使用。
关键是您必须重新分配变量,因此assert
实际上是一个身份函数,会在失败的类型断言时引发错误