Typescript negative type check

前端 未结 3 1904
故里飘歌
故里飘歌 2021-01-12 10:27

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?

相关标签:
3条回答
  • 2021-01-12 10:53

    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:

    • 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 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.

    0 讨论(0)
  • 2021-01-12 11:10

    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

    0 讨论(0)
  • 2021-01-12 11:12

    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!
    
    0 讨论(0)
提交回复
热议问题