typings / registry

The registry of type definitions for TypeScript
238 stars 176 forks source link

Typings for schema of a npm package's `package.json` #596

Open rozzzly opened 8 years ago

rozzzly commented 8 years ago

Okay, so I'm trying to find a type definition for the schema/deserialized content of a package.json file. You know the manifest as it were for an npm package, exists at the root of our projects, contains stuff like:

  "author": "rozzzly",
  "license": "MIT",
  "dependencies": {
    "immutable": "^3.8.1
  }

...but I can't find one. It's been like 45+ minutes and even though I'm a seasoned internets lurker, I still can't find it! I'm positive this must exist somewhere, its too common not to. But it's buried somewhere in hundreds of thousands of results referring to publishing npm packages, resolving type definitions, etc.

So far I've tried dozens search queries with google, github via org: typings _______ / org: DefinitelyTyped _______, and of course via the cli typings search _______.

I still can't find it. I know one of you out there has to have used it before, might you be kind enough to share a link?


Additionally, what can we do so other developers looking for the same thing have an easier time? From my point of view, this essentially means getting the desired .d.ts higher in Google search results. I've got a few thoughts on how this could be achieved, but want to hear from you first.

unional commented 8 years ago

Okay, so I'm trying to find a type definition for the schema/deserialized content of a package.json file.

Are you referring to the json schema for package.json? That's probably in schemastore.org.

Or are you looking for the typings of immutable? which you can install by typings install immutable

blakeembrey commented 8 years ago

@unional I'm pretty sure he's after a type definition for package.json.

We can add one to the registry, I've done something similar with other type definitions (like JSON schema). I know I would like a definition for package.json and bower.json, etc.

johnnyreilly commented 8 years ago

You can do a type definition for a json file? That I did not know! How does that get used? I mean json is basically a JavaScript object literal which can easily be typed. But how would that definition be applied to a JSON file?

rozzzly commented 8 years ago

@blakeembrey yes that is what I meant.

@johnnyreilly

how would that definition be applied to a JSON file?

To put it simply, just parse it into a plain old js object. Moreover, we can use an interface to describe an object, so by extension, a json schema can also be modeled with little difficulty.

Imagine we read package.json via readFile() from node's fs module. a string containing content of entire file JSON.parse() any ...wait, what?! that's not very useful. I'm lazy, I love autocompletion. And I love being able to use inline documentation to answer simple questions without having to do the following: switch to chrome. open new tab, google for somepackage's docs, choose the for the official docs, then ctrl+f to search relevant section of the docs docs.

With a proper definition, I get autocomplete for the schema which makes development quicker, and documentation which lets me find solutions easier. ...and I just realized I made the argument for having typing themselves! If it's something us developers interface with programmatically, we should have a type definition for it so we can interface with said data structure in the best way possible.

So the idea is to do something like

export interface npmPackageSchema {
    /** The name of the package. **/
    name: string;
    /** Optionally disallow npm from indexing this package**/
    private?: boolean;
    /** A list of the packages this package depends on. **/
    dependencies?: {
        /** A depended-upon package and its version (semver, filepath, etc) **/
        [pkgName: string]: string;
    };
    /**
     * Ensure that any "nonstandard" properties can
     * still be accessed without making a fuss. 
     **/
    [NonStandardProperty: string]: any;

};
const inspectPackage = (): npmPackageSchema => {
    // Some read file operations...

    //   You can assume the variable `content` is a string 
    //     with the contents of `package.json`.
    return JSON.parse(content); 
        // The type of the return value is `any`. It which undergoes implicit type 
        // assertion (aka a cast) to the return type of the  function, npmPackageSchema.
});

Then anything that uses the result of that function would get access to properties defined on that schema.

johnnyreilly commented 8 years ago

Nice @rozzzly :+1:

rozzzly commented 8 years ago

Because many different platforms, build targets, tools, plugins, or what-have-you use package.json as a centralized location for storing configuration data, one should not be surprised that their package.json file contains fields not described by the official schema. This shouldn't be too much of an issue thanks to type intersections.

please someone come up with a prettier naming convention for the interface :cold_sweat:

export interface npmPackageSchemaPlugin_ava {
    /**
     * Configuration options for `ava`
     **/
    ava: {
        /** Use the `tap` reporter? **/
        tap?: boolean;
        /** Number of worker processes used to run your tests. **/
        concurrency?: number;
        /** An array of glob expressions to use when searching for tests. **/
        files?: string[];
    };
}

const merged: npmPackageSchema & npmPackageSchemaPlugin_ava = inspectPackage() as any;

It would also be possible to define the the merged interface by creating a new interface which extends the base schema and the additions. fun fact: an interface can't implement anything but can extend multiple other interfaces and even classes! A class by comparison can implement any multiple interfaces, but can only extend one class.

export interface foo {
    bar?: boolean;
}
export interface bar {
    foo?: string;
}
export interface MySchema extends npmPackageSchema, foo, bar { 
    // would get kinda ugly if you have many schema mixed in,
    // ones with long identifiers moreover,
    // to have this just empty looks weird to me...
}

Perhaps the best solution could be:

export type MySchema = npmPackageSchema & foo  & bar;
// alternate way to format that...
export type MySchema = npmPackageSchema
    & npmPackageSchemaPlugin_foo
    & npmPackageSchemaPlugin_bar;
// personally I prefer this
export type MySchema = ((npmPackageSchema)
    & npmPackageSchemaPlugin_foo
    & npmPackageSchemaPlugin_bar
);
unional commented 8 years ago

I'm thinking of module augmentation in that case:

// official
declare module 'npm' {
  interface packageJson {
    ...
  }
}

// ava
declare module 'npm' {
  interface packageJson {
    // ava specific configuration
   }
}

Maybe put this under common/npm?

rozzzly commented 8 years ago

Ahhh even better, much more palatable. I only started using typescript a few months ago, haven't really used module in any of the typings I've written; sticking to the es6 import/export syntax.

blakeembrey commented 8 years ago

It might be better to keep this sort of thing global? It's tricky, but I wouldn't want to take the npm name in case someone actually wants to write the module for it (most likely). Using globals, though, you can just rely on general interface merging without module augmentation also.

declare interface PackageJson {
  main?: string
}

declare interface PackageJson {
  ...
}
blakeembrey commented 8 years ago

However, we could also provide npm's PackageJson interface as an actual export for the NPM typings if someone wants to start them.

rozzzly commented 8 years ago

And @blakeembrey, I agree about the name. It would be presumptuous to just randomly publish this with the name: npm. My thinking is:

johnnyreilly commented 8 years ago

Awesome gif!

unional commented 8 years ago

Would this help? https://github.com/bcherny/json-schema-to-typescript

ORESoftware commented 6 years ago

If we load an unknown package.json dynamically (not part of the build) it would be nice to have a pessimistic structure of how the JSON should look after parsing, something like this:

https://stackoverflow.com/questions/51794585/is-there-a-basic-ts-type-for-package-json