typescript 类Assert类型保护

yduiuuwa  于 2023-03-04  发布在  TypeScript
关注(0)|答案(5)|浏览(218)

在没有if的情况下,是否可以通过neverundefined(如Typescript中的assert)返回的函数调用来限制类型?
示例代码:

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
这在Typescript中可能吗?
我尝试过用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, ''
bmp9r5qi

bmp9r5qi1#

typescript 3.7添加了assertions in control flow analysis
asserts返回类型 predicate 指示函数仅在Assert成立时返回,否则将引发异常
消费者端不再需要黑客。

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();
}

Playground
此外,可以Assert所提供的参数为所需类型:

declare function assertIsArrayOfStrings(obj: unknown): asserts obj is string[];

function foo(x: unknown) {
    assertIsArrayOfStrings(x);
    return x[0].length;  // x has type string[] here
}

Playground

eoxn13cs

eoxn13cs2#

https://github.com/Microsoft/TypeScript/issues/8655的 typescript 积压工作中存在问题。因此,目前您无法执行此操作。
你能做的就是使用Assert操作符“!"。在value之后添加!将Assert该值既不是未定义的也不是空的。在你绝对确定它不会导致空或未定义的引用的情况下使用它。

function test() {
     const foo: (FooType|null) = getFoo();
     foo!.bar(); // "!" - asserts that foo is not null nor undefined
}

图片来源:https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-type-assertions

vawmfj5a

vawmfj5a3#

由于fooFoo | undefined,因此应该以某种方式将其类型更改为Foo
在上面的代码中,这可以通过以下方式合理地实现:

let foo = getFoo(); // Foo | undefined
foo = assertResultDefined(foo); // Foo
foo.bar();

另一种选择是使用非空Assert(正如另一个答案所建议的):

let foo = getFoo();
foo = assertResultDefined(foo);
foo = foo!;
foo.bar();
xbp102n0

xbp102n04#

这应该对你有用:

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实际上是一个标识函数,它在类型Assert失败时抛出错误

ykejflvf

ykejflvf5#

具有类型安全的一行程序:

function assert<T>(value: T | undefined): T {
  if (value === undefined) {
    throw new Error('value is undefined');
  }

  return value;
}

相关问题