TypeScript: Advanced Instance Type Helper
This challenge focuses on creating a powerful TypeScript utility type that can extract specific properties from an object type, similar to how you might access properties of an instance of a class. This is incredibly useful for defining interfaces and types that mirror existing object structures, promoting type safety and code maintainability.
Problem Description
Your task is to create a TypeScript conditional type named InstanceTypeHelper that accepts two type arguments:
T: The constructor type of a class or a function that returns an object.K: A union of string literal types representing the keys of the properties you want to extract from the instance created byT.
The InstanceTypeHelper should return a new object type containing only the properties specified by K from the instance created by T.
Key Requirements:
- The utility type must be a conditional type.
- It should correctly infer the return type of the constructor (
T) to access its instance properties. - It should extract only the properties whose keys are present in the union type
K. - If
Kcontains a key that does not exist on the instance type, that key should not be present in the resulting type.
Expected Behavior:
When applied to a constructor type and a set of keys, InstanceTypeHelper should produce a type that represents an object with precisely those keys and their corresponding value types from the instance.
Edge Cases to Consider:
- What happens if
Tis not a constructor type (e.g., a primitive type, an interface)? The type should ideally behave gracefully, perhaps by resulting in an empty object ornever. - What if
Kis an empty union type? The result should be an empty object type.
Examples
Example 1:
class User {
id: number;
name: string;
email: string;
constructor(id: number, name: string, email: string) {
this.id = id;
this.name = name;
this.email = email;
}
}
type UserInfo = InstanceTypeHelper<typeof User, 'id' | 'name'>;
/* Expected Output:
{
id: number;
name: string;
}
*/
Example 2:
interface Product {
productId: string;
price: number;
description: string;
stock: number;
}
// Simulate a "constructor-like" function that returns a Product
function createProduct(id: string, price: number, desc: string, stock: number): Product {
return { productId: id, price, description: desc, stock };
}
type ProductDetails = InstanceTypeHelper<typeof createProduct, 'price' | 'stock' | 'nonexistentKey'>;
/* Expected Output:
{
price: number;
stock: number;
}
*/
Example 3: Edge Case - Empty Keys
class DataPoint {
value: number;
timestamp: Date;
constructor(value: number, timestamp: Date) {
this.value = value;
this.timestamp = timestamp;
}
}
type EmptyData = InstanceTypeHelper<typeof DataPoint, never>;
/* Expected Output:
{}
*/
Constraints
- The solution must be written entirely in TypeScript.
- The solution should use conditional types.
- The solution should be efficient and not rely on runtime reflection or compilation-time tricks outside of TypeScript's type system.
- The solution should handle a reasonable number of properties and keys.
Notes
Consider how you can infer the type of the instance created by a constructor. TypeScript has built-in utility types that might be helpful here (though you are not strictly required to use them, understanding their purpose can guide you). Think about how to iterate over the keys of the inferred instance type and filter them based on the provided key union.