Is it possible to restrict typescript object to contain only properties defined by its class?

前端 未结 6 741
野趣味
野趣味 2020-11-27 06:32

Here is my code

async getAll(): Promise {
    return await dbQuery(); // dbQuery returns User[]
}

class User {
    id: number;
    na         


        
6条回答
  •  日久生厌
    2020-11-27 07:27

    Typescript can't restrict extra properties

    Unfortunately this isn't currently possible in Typescript, and somewhat contradicts the shape nature of TS type checking.

    Answers in this thread that relay on the generic NoExtraProperties are very elegant, but unfortunately they are unreliable, and can result in difficult to detect bugs.

    I'll demonstrate with GregL's answer.

    // From GregL's answer
    
    type Impossible = {
        [P in K]: never;
     };
    
     type NoExtraProperties = U & Impossible>;
    
     interface Animal {
        name: string;
        noise: string;
     }
    
     function thisWorks(animal: T & Impossible>): void {
        console.log(`The noise that ${animal.name.toLowerCase()}s make is ${animal.noise}.`);
     }
    
     function thisIsAsGoodAsICanGetIt(animal: NoExtraProperties): void {
        console.log(`The noise that ${animal.name.toLowerCase()}s make is ${animal.noise}.`);
     }
    
     const wrong2 = {
        name: 'Rat',
        noise: 'squeak',
        idealScenarios: ['labs', 'storehouses'],
        invalid: true,
     };
    
     thisWorks(wrong2); // yay, an error!
    
     thisIsAsGoodAsICanGetIt(wrong2); // yay, an error!
    

    This works if at the time of passing an object to thisWorks/thisIsAsGoodAsICanGet TS recognizes that the object has extra properties. But in TS if it's not an object literal, a value can always have extra properties:

    const fun = (animal:Animal) =>{
        thisWorks(animal) // No Error
        thisIsAsGoodAsICanGetIt(animal) // No Error
    }
    
    fun(wrong2) // No Error
    

    So, inside thisWorks/thisIsAsGoodAsICanGetIt you can't trust that the animal param doesn't have extra properties.

    Solution

    Simply use pick (Lodash, Ramda, Underscore).

    interface Narrow {
       a: "alpha"
    }
    
    interface Wide extends Narrow{
       b: "beta" 
    }
    
    const fun = (obj: Narrow) => {
       const narrowKeys = ["a"]
       const narrow = pick(obj, narrowKeys) 
       // Even if obj has extra properties, we know for sure that narrow doesn't
       ...
    }
    

提交回复
热议问题