In typescript: is it possible to check if type expected type IS NOT some type? Or create an interface that defines methods/props that should not be there?
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 assignable
func({ a: 1, b: 1, c: 2 }) // Error: type not assignable
funcGeneric<MyType>({ a: 1, b: 1, c: 2 }) // Error: type not assignable
Restrictions:
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.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.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 MyType
let 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);
}
}
Type guards : https://basarat.gitbooks.io/typescript/content/docs/types/typeGuard.html
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); // OK
foo(1); // OK
foo({ 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) // OK
process({}) // OK
process(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!'); // OK
greet('You silly sod!'); // Error!