如何从TypeScript类型守卫的类型谓词中提取目标类型?
Absolutely! You can absolutely pull out the target Type from a type guard's type definition using TypeScript's conditional types and the infer keyword, which lets us extract specific parts of a type structure. Let’s break down your desired logic and build each piece step by step:
1. Extract the Type Predicate from the Guard Function Type
First, we’ll create a TypePredicateOf utility type to grab the value is Type predicate from the type guard’s function type. This works by inferring the return type of the guard function:
type TypePredicateOf<Guard extends (value: unknown) => unknown> = Guard extends (value: unknown) => infer Predicate ? Predicate : never;
2. Extract the Guaranteed Type from the Type Predicate
Next, we’ll make an IsOf type that takes the predicate and extracts the core Type by matching the value is T structure:
type IsOf<Predicate> = Predicate extends value is infer T ? T : never;
3. Putting It All to the Test
Let’s use a real-world example to see this in action. Say we have a type guard for strings:
const isString = (value: unknown): value is string => typeof value === 'string';
Now apply our utility types exactly as you outlined:
// Get the full type guard function type type GuardType = typeof isString; // (value: unknown) => value is string // Extract the type predicate type StringPredicate = TypePredicateOf<GuardType>; // value is string // Extract the guaranteed type we care about type GuaranteedType = IsOf<StringPredicate>; // string
Shortcut: Combine into a Single Utility
If you don’t need the intermediate predicate step, you can merge both types into one for simplicity:
type ExtractGuardType<Guard extends (value: unknown) => value is unknown> = Guard extends (value: unknown) => value is infer T ? T : never;
Using this shortcut is even cleaner:
type StringType = ExtractGuardType<typeof isString>; // string
A Note on Generic Type Guards
If you’re working with a generic type guard (like one that checks for arrays of a specific type), the extracted type will retain the generic parameter. For example:
function isArrayOf<T>(value: unknown): value is T[] { return Array.isArray(value); } type ArrayGuardType = ExtractGuardType<typeof isArrayOf>; // T[]
To get a concrete type, just specify the generic parameter when referencing the guard:
type StringArrayType = ExtractGuardType<typeof isArrayOf<string>>; // string[]
内容的提问来源于stack exchange,提问作者Henrique Guerra

