最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to use negated types in TypeScript? - Stack Overflow

programmeradmin0浏览0评论

How can I use a negated or a "not" type in Typescript?

example(just an example), I tried to use is not in a type predicate:

function notUndef(obj: any): obj is not undefined {
// -------------------------------> ~~~ error!
// cannot find name 'not'.
    return obj !== void(0);
}

and

function notUndef(obj: any): (typeof obj !== undefined) {
//                   ')' expected -----> ~~~   -----> ~
//                                      ';' expected
    return obj !== void(0);
} 

but I received errors.

Neither of these work, what can I do?

How can I use a negated or a "not" type in Typescript?

example(just an example), I tried to use is not in a type predicate:

function notUndef(obj: any): obj is not undefined {
// -------------------------------> ~~~ error!
// cannot find name 'not'.
    return obj !== void(0);
}

and

function notUndef(obj: any): (typeof obj !== undefined) {
//                   ')' expected -----> ~~~   -----> ~
//                                      ';' expected
    return obj !== void(0);
} 

but I received errors.

Neither of these work, what can I do?

Share Improve this question edited Oct 30, 2022 at 18:23 jcalz 332k29 gold badges443 silver badges442 bronze badges asked Oct 30, 2022 at 16:08 Cflowe VisitCflowe Visit 3631 gold badge6 silver badges18 bronze badges 9
  • 1 What es after ':' is the return type of the function, you should set it to a specific type – Mohammed Commented Oct 30, 2022 at 16:13
  • You should specify return type as bool – Justinas Commented Oct 30, 2022 at 16:14
  • 3 TS doesn’t directly support negated types, so there is no “not” or the equivalent. For not undefined I would suggest {} | null instead, unless you have some specific use case where that doesn’t work? – jcalz Commented Oct 30, 2022 at 16:19
  • People suggesting that you use a return type are possibly unaware of the existence of type predicates? – jcalz Commented Oct 30, 2022 at 16:20
  • It looks like you were trying to use a type predicate. Did you actually look at the docs for that? There is no not syntax – Bergi Commented Oct 30, 2022 at 16:22
 |  Show 4 more ments

2 Answers 2

Reset to default 5

TypeScript does not directly support negated types, so there's no general way to express "the plement of T". There was an implementation of negated types at microsoft/TypeScript#29317 using the not keyword exactly as you are using it in your example. But this feature was never merged into the language.

There are workarounds which may or may not fit your use case. In the particular case of not undefined, you can write the equivalent type {} | null. The empty object-like type {} accepts every value except null or undefined (see this answer for more info), so the union of {} with null results in a type that accepts every value except undefined:

function notUndef(obj: any): obj is {} | null {
    return obj !== void (0);
}

const x = Math.random() < 0.5 ? "abc" : undefined;
if (notUndef(x)) {
    // x: string
    console.log(x.toUpperCase()); // "ABC" if reached
}

const y = ["abc", 123, { a: 456 }, null, undefined][
    Math.floor(Math.random() * 5)];
// const y: string | null | {a: number} | null | undefined
if (notUndef(y)) {
    y // y: string | null | {a: number} | null
} else {
    y // y: undefined
}

In general, if the type you'd like to negate can be filtered out of a union type, then you can use a generic type parameter and the Exclude<T, U> utility type to filter the to-be-negated type out of whatever union is passed in. Here's a way to "negate" an existing user-defined type guard function (as long as filtering a union works with it):

function not<U>(guard: (obj: any) => obj is U) {
    return <T,>(obj: T): obj is Exclude<T, U> => !guard(obj);
}

Let's test it with undefined:

function isUndefined(obj: any): obj is undefined {
    return obj === void (0);
}
const notUndef = not(isUndefined);
// const notUndef: <T>(obj: T) => obj is Exclude<T, undefined>
const x = Math.random() < 0.5 ? "abc" : undefined;
if (notUndef(x)) {
    // x: string
    x.toUpperCase();
}

And how about with number:

function isNumber(obj: any): obj is number {
    return typeof obj === "number";
}
const notNumber = not(isNumber);
// const notNumber: <T>(obj: T) => obj is Exclude<T, number>

const y = Math.random() < 0.5 ? "abc" : 123;
if (notNumber(y)) {
    // y: "abc"
    y.toUpperCase();
} else {
    y.toFixed();
    // y: 123
}

So that works.


There are limits to these approaches, though. Some types that cannot be negated trivially. Imagine you had

interface Foo { a: number, b: string }

and you wanted to write not Foo. You could manually build up a type like:

interface Foo { a: number, b: string }
type AllTypes = string | boolean | bigint | null | undefined | symbol | object | number;
type NotFoo =
    Exclude<AllTypes, object> |
    { a?: Exclude<AllTypes, number>, [k: string]: any } |
    { b?: Exclude<AllTypes, string>, [j: string]: any }

And I think it works:

let nf: NotFoo;
nf = 3;
nf = {};
nf = { a: 2, b: 3, c: 10 } // okay
nf = { a: "", b: "", c: 10 } // okay
nf = { a: 2, b: "", c: 10 } // error

but it doesn't scale well. You could use the generic Exclude version, which works well when you pass in a union of known things:

function isFoo(obj: any): obj is Foo {
    return obj && typeof obj === "object" &&
        "a" in obj && typeof obj.a === "number" &&
        "b" in obj && typeof obj.b === "string"
}
const notFoo = not(isFoo);
// const notFoo: <T>(obj: T) => obj is Exclude<T, Foo>

const y = [3, {}, {a: 2, b: 3, c: 10}, {a: "", b: "", c: 10}, {a: 2, b: "", c: 10}][
    Math.floor(Math.random() * 5)];
if (notFoo(y)) {
    y 
    // const y: number | 
    // { a?: undefined; b?: undefined; c?: undefined; } | 
    // { a: number; b: number; c: number; } | 
    // { a: string; b: string; c: number; }
} else {
    y // const y: {  a: number; b: string; c: number; }
}

but fails when you are passing in a type where you can't just filter out some of the members, such as the unknown type:

const z: unknown = "
发布评论

评论列表(0)

  1. 暂无评论