最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Typescript - Check an object has all interface properties - Stack Overflow

programmeradmin4浏览0评论

Say I have some interface:

export interface MyDocument {
    id: string,
    collection: string[];
}

I then create a new (well cast an existing to that type):

const workingDocument = <MyDocument>document;

And finally, I have this if statement block to check whether it actually contains everything I have specified in that interface:

if (!workingDocument.id) {
   throw new Error("Document missing `id`");
} else if (!workingDocument.collection) {
   throw new Error("Document missing `collection` array");
}

However I don't seem to like this as that if statement could grow forever and not very good to maintain.

Is there a better way?

Thanks.

Say I have some interface:

export interface MyDocument {
    id: string,
    collection: string[];
}

I then create a new (well cast an existing to that type):

const workingDocument = <MyDocument>document;

And finally, I have this if statement block to check whether it actually contains everything I have specified in that interface:

if (!workingDocument.id) {
   throw new Error("Document missing `id`");
} else if (!workingDocument.collection) {
   throw new Error("Document missing `collection` array");
}

However I don't seem to like this as that if statement could grow forever and not very good to maintain.

Is there a better way?

Thanks.

Share Improve this question asked May 14, 2019 at 0:47 userMod2userMod2 8,96017 gold badges73 silver badges133 bronze badges 2
  • 1 The worst abuse I saw of typescript was a class with thisin the constructor: constructor(data: any) { Object.assign(this,data); } - voila - whatever you pass into this class automatically adheres to whatever properties are on the class, according to the compiler....runtime is a different story. Typescript is supposed to catch potential problems at compile time, use it for it's strengths. If you are casting and you don't know for sure what you are casting, and end up writing ifs to check, then you're doing typescript wrong. – Adam Jenkins Commented May 14, 2019 at 0:52
  • If all properties which are declared on interface are not defined in it's concrete class then ideally typescript transpiler throws an error to do same. Ideally you should not check value is present or not with if..else statement. – Aamol Commented May 14, 2019 at 1:04
Add a comment  | 

2 Answers 2

Reset to default 13

Original Answer

If I understand correctly, you're asking for a runtime check that an object includes all the properties that an interface defines. That is not possible with an interface on its own, because the type information associated with an interface does not make it to runtime; in other words, the interface is only useful when we run the TypeScript compiler.

What you could do is to create an schema that contains all the properties of the interface. Then you could loop over that schema to check that all the properties exist on your object. Here is an example of how that might look. I have wrapped the example in a user-defined type guard.

export interface MyDocument {
    id: string,
    collection: string[];
}

const isMyDocument = (input: any): input is MyDocument => {

    const schema: Record<keyof MyDocument, string> = {
        id: 'string',
        collection: 'array'
    };

    const missingProperties = Object.keys(schema)
        .filter(key => input[key] === undefined)
        .map(key => key as keyof MyDocument)
        .map(key => new Error(`Document is missing ${key} ${schema[key]}`));

    // throw the errors if you choose

    return missingProperties.length === 0;
}

const obj = {};

if (isMyDocument(obj)) {
  // the compiler now knows that obj has all of its properties
  obj.collection;
} 

Here is the above code in the TypeScript playground.

Answer to Question in the Comments

Here is how you might use the ... operator to extend a schema.

interface ParentDocument { 
    id: string,
    collection: [],
}

interface ChildDocument extends ParentDocument { 
    name: string;
}

const schemaParent: Record<keyof ParentDocument, string> = {
    id: 'string',
    collection: 'array'
};

const schemaChild: Record<keyof ChildDocument, string> = {
    name: 'string',
    ...schemaParent,
};

If you're creating / using this Document type internally, you can use types/interfaces to assert their types - for yourself - without the need to cast.

However, if the documents are from outside of your typescript app, you'll need to result to some form of manual type guarding / checking (the thing you want to avoid).

发布评论

评论列表(0)

  1. 暂无评论