Typescript negative type check

前端 未结 3 1929
故里飘歌
故里飘歌 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 = ({[P in T]: P } & {[P in U]: never } & { [x: string]: never })[T];  
    type Omit = Pick>;
    type AnyTypeWithoutCorD = Omit
    

    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(arg: AnyTypeWithoutCorD) {}
    
    // Accepts a parameter of a known type MyType, constrained 
    // into the AnyTypeWithoutCorD type.
    function func(arg: AnyTypeWithoutCorD) {}
    
    let foo: AnyTypeWithoutCorD = { a: 1, b: 1, c: 2 } // Error: type not assignable
    func({ a: 1, b: 1, c: 2 }) // Error: type not assignable
    funcGeneric({ 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 = { a: 1, b: 1, c: 2 }
    let ok: AnyTypeWithoutCorD<{ c: number }> = { a: 1, b: 1, c: 2 }
    let ok: AnyTypeWithoutCorD = { a: 1, b: 1, c: 2 } as MyType
    let ok: AnyTypeWithoutCorD = 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.

    提交回复
    热议问题