Typescript negative type check Typescript negative type check typescript typescript

Typescript negative type check


Disallowing specific properties

Use the never type to tell TypeScript a property should not exist on an object.

interface Nameless extends Object {    name?: never;}

Usage:

declare function foo<T extends Nameless>(argument: T): void;foo(document);        // OKfoo(1);               // OKfoo({ name: 'Bob' }); // Error

Negating object types

Use the Subtract helper:

type Subtract<T, U> = T & Exclude<T, U>

Usage:

declare function process<T>(argument: Subtract<T, Window>): void;process(1)      // OKprocess({})     // OKprocess(window) // Error

Negating literals

Use the Subtract helper again:

type Subtract<T, U> = T & Exclude<T, U>

Usage:

type Insult =  | 'You silly sod!'  | 'You son of a motherless goat!';declare function greet<T extends string>(greeting: Subtract<T, Insult>): void;greet('Hello, good sir!'); // OKgreet('You silly sod!');   // Error!


In the TypeScript compiler it is not yet possible to do this, though it might be possible eventually.

This issue on the TypeScript project seems to be the main one tracking this feature request; many other similar issues are linked to it.

It is as always possible to do these checks at runtime. This can be done manually via standard checks/assertions. I personally like using JSONSchema for non-trivial "everything except objects shaped like X" cases, or the io-ts package, to build these types of runtime validators. In TypeScript, you also have access to type guard functions, which can be used to perform these validations.

EDIT this is possible in a limited, not very useful way. Using a modification of the Omit type from this article the type checker can be made to reject some, but not all, violating types.

For example, let's say you wanted a type for "any object that does not have properties c or d". You could express that type thus:

type Diff<T extends string, U extends string> = ({[P in T]: P } & {[P in U]: never } & { [x: string]: never })[T];  type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>;type AnyTypeWithoutCorD<T> = Omit<T, keyof T & ( "c" | "d" )>

Using that type, the following is rejected by the typechecker:

type MyType = { a: number, b: number, c: number };// Accepts a generic argument and a parameter; ensures the// parameter can be constrained into the AnyTypeWithoutCorD type.function funcGeneric<T>(arg: AnyTypeWithoutCorD<T>) {}// Accepts a parameter of a known type MyType, constrained // into the AnyTypeWithoutCorD type.function func(arg: AnyTypeWithoutCorD<MyType>) {}let foo: AnyTypeWithoutCorD<MyType> = { a: 1, b: 1, c: 2 } // Error: type not assignablefunc({ a: 1, b: 1, c: 2 }) // Error: type not assignablefuncGeneric<MyType>({ a: 1, b: 1, c: 2 }) // Error: type not assignable

Restrictions:

  • All keys in the input type (MyType in the example) must be explicitly enumerated. No index property signatures are allowed; those cause the typechecker to accept Omit objects regardless of their fields. For example, if you add [x: string]: any into the specification of MyType in the above example, the typechecker happily accepts all of the arguments ( & { [x: string]: any } is equivalent and behaves the same way). If you're performing Omit-type validation on things whose input types you don't control, this means that your validation won't take place if any input types have index property signatures.
  • This only works if you are supplying a brand new value to the Omit type constructor, or doing something equivalently "early" on in type checking. Supplying data from a previous assignment, or via type assertion does not perform the Omit validation as expected.
  • If you supply raw data to a generic function or type that uses the generic to parameterize an Omit type, and you do not explicitly state the generic when calling the function, it will not be correctly inferred, and as a result Omit validation will not occur since your type will not pass.

For example, none of the below work (they are all accepted by the type checker):

let bar: MyType = { a: 1, b: 1, c: 2 }func(bar)func({ a: 1, b: 1, c: 2 } as MyType)funcGeneric(bar)funcGeneric({ a: 1, b: 1, c: 2 })let ok: AnyTypeWithoutCorD<object> = { a: 1, b: 1, c: 2 }let ok: AnyTypeWithoutCorD<{ c: number }> = { a: 1, b: 1, c: 2 }let ok: AnyTypeWithoutCorD<MyType> = { a: 1, b: 1, c: 2 } as MyTypelet ok: AnyTypeWithoutCorD<MyType> = bar as MyType

These are my first attempts at achieving/demonstrating this, so folks more knowledgeable about TypeScript and custom type construction may correct me. Take it with a grain of salt.

Conclusions:

The Omit solution is not worth it.

Unless you control all input types to your Omit receivers, and are maintain discipline in keeping those input types free of index property signatures, and also ensure that every time you supply anything to one of those receivers it is actually picking up the Omit constraint, this will do more harm than good.

This is because it will sometimes correctly validate your types, which creates a false sense of security and difficult-to-debug, seemingly inconsistent behavior.

The issue linked in the very first part of the question, if completed, will result in a much more robust, predictable, and documented solution for this use case. Until then, use runtime checks.


In typescript: is it possible to check if type expected type IS NOT some type?

I suspect what you are looking for is runtime types. TypeScript doesn't have a way to do this since there isn't a Standardized way to do this in JavaScript either.

If you are just looking for simple type guards they work fine e.g.

function foo(bar: string | number){    if (typeof bar !== 'string') {        // TypeScript knows its a number!        return bar.toPrecision(3);    }}

More

Type guards : https://basarat.gitbooks.io/typescript/content/docs/types/typeGuard.html