TypeScript require generic parameter to be provided

前端 未结 4 1110
长发绾君心
长发绾君心 2021-01-07 20:54

I have the following function:

async function get(url: string): Promise {
    return getUrl(url);
}

However, i

相关标签:
4条回答
  • 2021-01-07 21:00

    Yet another approach! Require a parameter that is used just for the type inference:

    /**
     * getFoo gives you a well-typed Foo component
     * ```tsx
     * const foo = getFoo(config)
     * ```
     */
    const getFoo = <Keys extends string>(
      _config: Record<Keys, unknown>
    ) => {
      return FooUntyped as FooType<Keys>
    }
    

    If you have objects readily available to pass in for this type inference, and your functions wouldn't otherwise accept a parameter, this may be a good approach for you *as of TS v4.1.2

    If you don't have some config object readily available, calling it is a little bit "wtf" but you get an error directly where you call it, instead of somewhere further on:

    const foo = getFoo({} as Record<'my' | 'keys', unknown>)
    

    This design/approach happened to actually fix a little kink in my design in another location. I actually added an extra param too my getFoo called defaultProps which are some default props for a given Foo component (Foo is a Form Field component, and config keys are valid field names)

    0 讨论(0)
  • 2021-01-07 21:02

    You could use multiple type parameters:

    function contractType<T = void, U extends T = T>(value: unknown): U {
        return value as U
    }
    
    const example1: string = contractType(17) // error
    const example2: string = contractType("value") // error
    const example3: string = contractType<string>("value") // ok
    

    https://github.com/Microsoft/TypeScript/issues/14829#issuecomment-288902999

    0 讨论(0)
  • 2021-01-07 21:07

    There is no built-in support for this, we can however engineer a scenario where not passing in a type parameter will generate an error, using default generic type arguments and conditional types. Namely we will give U a default value of void. If the default value is the actual value of U, then we will type the parameter to the function as something that should not really be passed in so as to get an error:

    async function get<U = void>(url: string & (U extends void ? "You must provide a type parameter" : string)): Promise<U> {
        return null as any;
    }
    
    get('/user-url'); // Error Argument of type '"/user-url"' is not assignable to parameter of type '"You must provide a type parameter"'.
    
    class User {}
    get<User>('/user-url');
    

    The error message is not ideal, but I think it will get the message across.

    Edit: For a solution where the type parameter is used in parameter types see here

    0 讨论(0)
  • 2021-01-07 21:07
    function createCommand<P extends WhatEver = never>(a: P): P {
        a.whatEverProperty; // a is always instanceof WhatEver. Never is overlooked here.
    }
    

    The easiest solution for most of cases is specifying never as default type argument value. never is always assignable to the type argument, even if you extend it with WhatEver, so the body of function is not affected, but the return value here will be never affecting the consumers ability to use this function in any meaningful way.

    (Consumers/callers will get an error at the point they try to use the return value, but if your function is called purely for side-effects, you may want to consider the approach outlined in @Titian's answer)

    You can use conditional types to make the return type never when you normally wouldn't return P:

    function createCommand<P extends WhatEver = never>(a: P): P['whatEverProperty'] {
        // a is always instanceof WhatEver. Never is overlooked here.
        return a.whatEverProperty as P extends WhatEver ?
          ? WhatEver
          : never
    }
    
    0 讨论(0)
提交回复
热议问题