TypeScript Infer Keyword Helpers: Type Deduction Utilities
TypeScript's infer keyword is a powerful tool for extracting types from within other types, enabling advanced conditional type manipulation. This challenge focuses on creating reusable utility types that leverage infer to simplify common type extraction tasks. You'll build helper types that can parse and extract specific parts of complex types, making your TypeScript code more readable and maintainable.
Problem Description
Your task is to create a set of generic TypeScript utility types that utilize the infer keyword to extract specific type information from various common type structures. These helpers should be robust enough to handle different scenarios and provide clear, predictable type outcomes.
Key Requirements:
- Extracting Return Type: Create a utility type
ExtractReturnType<T>that infers the return type of a function. - Extracting Parameter Types: Create a utility type
ExtractParamTypes<T>that infers a tuple of all parameter types of a function. - Extracting Element Type from Array/Promise: Create a utility type
Unwrap<T>that can unwrap the element type from an array (T[]orArray<T>), a Promise (Promise<T>), or a simple type. If it's neither an array nor a Promise, it should return the type itself. - Extracting Property Type: Create a utility type
ExtractPropType<T, K>that infers the type of a specific propertyKfrom an object typeT.
Expected Behavior:
The utility types should behave as described above when applied to function, array, promise, and object types.
Edge Cases to Consider:
- Functions with no parameters.
- Functions with optional parameters.
- Functions with rest parameters.
- Arrays of various types.
- Nested arrays or promises (for
Unwrap). - Accessing non-existent properties (for
ExtractPropType).
Examples
Example 1: Extracting Return Type
// Input
type FunctionWithReturn = () => string;
type Func1 = FunctionWithReturn;
// Expected Output
type ReturnTypeOfFunc1 = ExtractReturnType<Func1>; // Should be 'string'
// Input
type AsyncFunction = () => Promise<number>;
type Func2 = AsyncFunction;
// Expected Output
type ReturnTypeOfFunc2 = ExtractReturnType<Func2>; // Should be 'Promise<number>'
Example 2: Extracting Parameter Types
// Input
type FunctionWithParams = (a: number, b: string, c?: boolean) => void;
type Func3 = FunctionWithParams;
// Expected Output
type ParamTypesOfFunc3 = ExtractParamTypes<Func3>; // Should be '[a: number, b: string, c?: boolean | undefined]' (or similar tuple representation)
// Input
type NoParamFunction = () => void;
type Func4 = NoParamFunction;
// Expected Output
type ParamTypesOfFunc4 = ExtractParamTypes<Func4>; // Should be '[]' (empty tuple)
Example 3: Unwrapping Types
// Input
type MyArray = number[];
type MyPromise = Promise<string>;
type MySimpleType = boolean;
// Expected Output
type UnwrappedArray = Unwrap<MyArray>; // Should be 'number'
type UnwrappedPromise = Unwrap<MyPromise>; // Should be 'string'
type UnwrappedSimple = Unwrap<MySimpleType>; // Should be 'boolean'
type UnwrappedNestedPromise = Unwrap<Promise<number[]>>; // Should be 'number[]'
Example 4: Extracting Property Type
// Input
type User = {
id: number;
name: string;
isActive: boolean;
};
// Expected Output
type UserIDType = ExtractPropType<User, 'id'>; // Should be 'number'
type UserNameType = ExtractPropType<User, 'name'>; // Should be 'string'
type NonExistentProp = ExtractPropType<User, 'email'>; // Should be 'undefined' (or similar representation for absence)
Constraints
- The solution must be implemented entirely in TypeScript.
- Each utility type should be generic and accept a type parameter.
- The solution should strive for clarity and readability.
- There are no strict performance constraints for type manipulation at compile time, but efficient and direct type definitions are preferred.
Notes
- The
inferkeyword can only be used within conditional types (extends). - For function parameter types, you'll need to handle the
...args: infer Ppattern. - For
Unwrap, consider using multiple conditional checks to handle arrays (T extends (infer U)[]), promises (T extends Promise<infer U>), and then a fallback. - For
ExtractPropType, consider how to represent the case where a property does not exist. You might useundefinedor a custom type representing "not found".