如何让TypeScript从普通JavaScript对象推断类型?库开发问询
Absolutely, this is totally doable! You can deliver full type safety and IntelliSense for both TypeScript and JavaScript users by leveraging TypeScript's advanced type system features. Let's break down how to implement this:
1. TypeScript User Experience
For TS users who explicitly pass a generic type (like Collection<Person>), we can build the Collection class to enforce the type on the object returned by get().
Implementation Code
// Generic Collection class to enforce type safety class Collection<T> { get(id: number): T { // Replace with your actual database fetch logic return {} as T; // Type assertion used for demonstration } } // User-defined interface matching their schema interface Person { firstName: string; age?: number; } // User's usage const personCollection = new Collection<Person>(); personCollection.get(12).firstName; // ✅ No errors, full IntelliSense personCollection.get(12).test; // ❌ TS compilation error (property 'test' doesn't exist on type 'Person')
This works because the generic type T is strictly enforced on the return value of get(), so TypeScript validates all property accesses against the user's defined type.
2. JavaScript User Experience
For JS users who only provide a Schema object, we can use TypeScript's type inference from object literals and mapped types to generate a type from the Schema. VSCode will use this generated type to show IntelliSense and warn about invalid properties.
Step 1: Define Schema Base Types
First, we need to type the Schema structure so TypeScript can interpret it correctly:
type PropertyDefinition = { name: string; type: 'string' | 'number'; primary?: boolean; required?: boolean; }; type DatabaseSchema = { name: string; properties: PropertyDefinition[]; };
Step 2: Convert Schema to Entity Type
We'll create a mapped type that transforms the Schema into a corresponding object type, respecting required/optional flags and property types:
type SchemaToEntityType<S extends DatabaseSchema> = { [PropName in S['properties'][number]['name']]: // Check if the property is marked as required PropName extends Extract<S['properties'][number], { name: PropName; required: true }> // Map required properties directly to their type ? Extract<S['properties'][number], { name: PropName }>['type'] extends 'string' ? string : number // Mark optional properties as possibly undefined : Extract<S['properties'][number], { name: PropName }>['type'] extends 'string' ? string | undefined : number | undefined; };
Step 3: Build a Type-Inferred Collection Factory
Instead of having users instantiate Collection directly, we'll create a factory function that accepts a Schema and automatically infers the entity type:
class Collection<T = unknown> { private schema?: DatabaseSchema; constructor(schema?: DatabaseSchema) { this.schema = schema; // Optional: Add runtime validation logic here using the schema } get(id: number): T { // Replace with your actual database fetch logic return {} as T; // Type assertion for demonstration } } // Factory function to enable automatic type inference function createCollection<S extends DatabaseSchema>(schema: S): Collection<SchemaToEntityType<S>> { return new Collection(schema) as Collection<SchemaToEntityType<S>>; }
JavaScript User Usage
Now JS users can pass their Schema object and get full IntelliSense in VSCode:
// JS user's schema definition const personSchema = { name: 'person', properties: [ { name: 'firstName', type: 'string', primary: true, required: true }, { name: 'age', type: 'number', required: false } ] }; const personCollection = createCollection(personSchema); personCollection.get(12).firstName; // ✅ IntelliSense shows this property personCollection.get(12).test; // ❌ VSCode warns "property 'test' does not exist"
Even though JavaScript doesn't compile to enforce this, VSCode uses TypeScript's language service to analyze the Schema object and provide accurate hints—exactly what you're aiming for.
Bonus: Optional Runtime Validation
For JS users, you can add runtime checks in the get() method to ensure returned objects only contain schema-defined properties, or throw errors when accessing invalid properties (though this isn't required for the IntelliSense functionality).
内容的提问来源于stack exchange,提问作者Jeff Huijsmans

