├── .editorconfig ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .prettierrc ├── .travis.yml ├── .vscode └── settings.json ├── CHANGELOG.md ├── README.md ├── index.ts ├── nodemon.json ├── package.json ├── renovate.json ├── tsconfig.json ├── tslint.json ├── type-tests ├── errors.test.ts ├── index.d.ts ├── links.test.ts ├── meta.test.ts ├── relationships.test.ts ├── resources.test.ts ├── toplevel-collection.test.ts ├── toplevel-single.test.ts ├── toplevel.test.ts ├── tsconfig.json └── tslint.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | indent_style = tab 11 | indent_size = 4 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **System Information (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - TypeScript Version [e.g. 22] 27 | 28 | **Additional context** 29 | Add any other context about the problem here. 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "typescript", 3 | "singleQuote": true, 4 | "semi": true, 5 | "tabWidth": 2, 6 | "useTabs": true 7 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 10 3 | 4 | cache: 5 | yarn: true 6 | 7 | before_install: 8 | - curl -o- -L https://yarnpkg.com/install.sh | bash 9 | - export PATH=$HOME/.yarn/bin:$PATH 10 | 11 | install: 12 | - yarn install --no-lockfile --non-interactive 13 | 14 | stages: 15 | - name: 'Conventional Commits' 16 | if: type = push 17 | - 'Basic Tests' 18 | - 'Compatibility Tests' 19 | - name: 'Deploy' 20 | if: branch = master AND type = push 21 | 22 | jobs: 23 | fail_fast: true 24 | include: 25 | - stage: 'Conventional Commits' 26 | name: 'Conventional Commits' 27 | script: commitlint-travis 28 | 29 | - stage: 'Basic Tests' 30 | name: 'Fixed Dependencies' 31 | install: yarn install --non-interactive 32 | 33 | - stage: 'Compatibility Tests' 34 | name: 'Floating Dependencies' 35 | - &ts-version-test 36 | env: TS_VERSION=2.8 37 | before_script: yarn add -D "typescript@$TS_VERSION" 38 | - <<: *ts-version-test 39 | env: TS_VERSION=3.0 40 | - <<: *ts-version-test 41 | env: TS_VERSION=latest 42 | - <<: *ts-version-test 43 | env: TS_VERSION=next 44 | 45 | - stage: 'Deploy' 46 | name: 'Publish to npm' 47 | install: 48 | - yarn install --non-interactive 49 | script: yarn semantic-release 50 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "editor.formatOnSave": true 4 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.1.3](https://github.com/mike-north/jsonapi-typescript/compare/v0.1.2...v0.1.3) (2019-04-10) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * lint fixes ([3f18d08](https://github.com/mike-north/jsonapi-typescript/commit/3f18d08)) 7 | * v0.1.2 ([0a87710](https://github.com/mike-north/jsonapi-typescript/commit/0a87710)) 8 | 9 | ## [0.1.1](https://github.com/mike-north/jsonapi-typescript/compare/v0.1.0...v0.1.1) (2019-04-10) 10 | 11 | 12 | ### Bug Fixes 13 | 14 | * add meta to the DocBase type ([e63612e](https://github.com/mike-north/jsonapi-typescript/commit/e63612e)) 15 | * use dtslint for tests ([a163180](https://github.com/mike-north/jsonapi-typescript/commit/a163180)) 16 | 17 | # [0.1.0](https://github.com/mike-north/jsonapi-typescript/compare/v0.0.8...v0.1.0) (2018-11-07) 18 | 19 | 20 | ### Bug Fixes 21 | 22 | * auto-publish on PR merge ([2b8fdeb](https://github.com/mike-north/jsonapi-typescript/commit/2b8fdeb)) 23 | 24 | 25 | ### Features 26 | 27 | * update README [skip ci] ([b39ced1](https://github.com/mike-north/jsonapi-typescript/commit/b39ced1)) 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSONAPI-typescript 2 | 3 | [![Build Status](https://travis-ci.org/mike-north/jsonapi-typescript.svg?branch=master)](https://travis-ci.org/mike-north/jsonapi-typescript) 4 | [![Version](https://img.shields.io/npm/v/jsonapi-typescript.svg)](https://www.npmjs.com/package/jsonapi-typescript) 5 | 6 | TypeScript type information for compile-time validation of [JSON:API documents](https://www.jsonapi.org/). Supports TS 2.3 and above. 7 | 8 | ## How to use this 9 | 10 | 1. Install this package 11 | ```js 12 | npm install --save-dev jsonapi-typescript 13 | ``` 14 | 15 | 2. Import this module 16 | ```ts 17 | import * as JSONAPI from 'jsonapi-typescript'; 18 | ``` 19 | 20 | 3. check to see if json types are validated correctly 21 | 22 | ```ts 23 | import * as JSONAPI from 'jsonapi-typescript'; 24 | 25 | // ✅ This should be OK 26 | let doc: JSONAPI.Document = { 27 | data: { 28 | type: 'articles', 29 | id: '1' 30 | } 31 | }; 32 | 33 | // ⛔️ This should NOT be OK ("result" is not a valid JSON:API top-level key) 34 | let doc: JSONAPI.Document = { 35 | result: "Success!" 36 | }; 37 | 38 | // ⛔️ This should NOT be OK ( empty Array is not a valid JSON:API document ) 39 | let doc: JSONAPI.Document = []; 40 | ``` 41 | 42 | ## Copyright 43 | © 2017 [Mike North](https://github.com/mike-north), All Rights Reserved. 44 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import * as JSON from 'json-typescript'; 2 | /** 3 | * A JSON object MUST be at the root of every JSON API request and responsecontaining data. 4 | * This object defines a document’s “top level”. 5 | * A document MUST contain at least one of the following top-level members: 6 | */ 7 | 8 | export type MetaObject = JSON.Object; 9 | 10 | /** 11 | * this type is no longer required, as the meta has been moved to the DocBase 12 | * this type can be safely removed in future versions 13 | */ 14 | export interface DocWithMeta extends DocBase { 15 | meta: MetaObject; // a meta object that contains non-standard meta-information. 16 | } 17 | 18 | export interface DocWithData 19 | extends DocBase { 20 | data: T; // the document’s “primary data” 21 | included?: Included; 22 | } 23 | 24 | export interface DocWithErrors extends DocBase { 25 | errors: Errors; // an array of error objects 26 | } 27 | // The members data and errors MUST NOT coexist in the same document. 28 | // ⛔️ NOT EXPRESSIBLE IN TYPESCRIPT 29 | // If a document does not contain a top-level data key, 30 | // the included member MUST NOT be present either. 31 | // ⛔️ NOT EXPRESSIBLE IN TYPESCRIPT 32 | 33 | /* A document MAY contain any of these top-level members: */ 34 | export interface DocBase { 35 | jsonapi?: ImplementationInfo; 36 | links?: Links | PaginationLinks; 37 | meta?: MetaObject; // a meta object that contains non-standard meta-information. 38 | } 39 | 40 | export type Document = DocWithErrors | DocWithMeta | DocWithData; 41 | export type SingleResourceDoc< 42 | T extends string = string, 43 | A extends { [k: string]: JSON.Value } = { [k: string]: JSON.Value } 44 | > = DocWithData>; 45 | export type CollectionResourceDoc< 46 | T extends string = string, 47 | A extends { [k: string]: JSON.Value } = { [k: string]: JSON.Value } 48 | > = DocWithData>>; 49 | 50 | // an object describing the server’s implementation 51 | export interface ImplementationInfo { 52 | version?: string; 53 | meta?: MetaObject; 54 | } 55 | 56 | export type Link = string | { href: string; meta?: MetaObject }; 57 | 58 | // The top-level links object MAY contain the following members: 59 | export interface Links { 60 | self?: Link; // the link that generated the current response document. 61 | related?: Link; // a related resource link when the primary data represents a resource relationship. 62 | // TODO pagination links for the primary data. 63 | } 64 | 65 | export interface PaginationLinks { 66 | first?: Link | null; // the first page of data 67 | last?: Link | null; // the last page of data 68 | prev?: Link | null; // the previous page of data 69 | next?: Link | null; // the next page of data 70 | } 71 | 72 | export type Included = ResourceObject[]; 73 | 74 | export interface ErrorObject { 75 | id?: number | string; 76 | links?: Links; 77 | status?: string; 78 | code?: string; 79 | title?: string; 80 | detail?: string; 81 | source?: { 82 | pointer?: any; 83 | parameter?: string; 84 | }; 85 | meta?: MetaObject; 86 | } 87 | 88 | export type PrimaryData< 89 | T extends string = string, 90 | A extends AttributesObject = AttributesObject 91 | > = ResourceObject | Array>; 92 | 93 | export interface ResourceObject< 94 | T extends string = string, 95 | A extends AttributesObject = AttributesObject 96 | > { 97 | id?: string; 98 | type: T; 99 | attributes?: AttributesObject; 100 | relationships?: RelationshipsObject; 101 | links?: Links; 102 | meta?: MetaObject; 103 | } 104 | 105 | export interface ResourceIdentifierObject { 106 | id: string; 107 | type: string; 108 | meta?: MetaObject; 109 | } 110 | 111 | export type ResourceLinkage = 112 | | null 113 | | never[] 114 | | ResourceIdentifierObject 115 | | ResourceIdentifierObject[]; 116 | 117 | export interface RelationshipsWithLinks { 118 | links: Links; 119 | } 120 | 121 | export interface RelationshipsWithData { 122 | data: ResourceLinkage; 123 | } 124 | 125 | export interface RelationshipsWithMeta { 126 | meta: MetaObject; 127 | } 128 | 129 | export type RelationshipObject = 130 | | RelationshipsWithData 131 | | RelationshipsWithLinks 132 | | RelationshipsWithMeta; 133 | 134 | export interface RelationshipsObject { 135 | [k: string]: RelationshipObject; 136 | } 137 | 138 | export type AttributesObject< 139 | ATTRS extends { [k: string]: JSON.Value } = { [k: string]: JSON.Value } 140 | > = { [K in keyof ATTRS]: ATTRS[K] }; 141 | 142 | export type Errors = ErrorObject[]; 143 | -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "exec": "./node_modules/.bin/ts-node", 3 | "watch": [ 4 | "tests/**/*.ts", 5 | "tests/tsconfig.json", 6 | "tests/mocha.opts", 7 | "tsconfig.json", 8 | "index.ts" 9 | ], 10 | "ext": "ts js json" 11 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsonapi-typescript", 3 | "version": "0.1.3", 4 | "description": "TypeScript definitions for JSON-API", 5 | "main": "index.ts", 6 | "scripts": { 7 | "test": "dtslint type-tests", 8 | "lint": "./node_modules/.bin/tslint -p .", 9 | "watch": "./node_modules/.bin/nodemon $(which npm) test", 10 | "semantic-release": "semantic-release" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/mike-north/jsonapi-typescript.git" 15 | }, 16 | "keywords": [ 17 | "jsonapi", 18 | "typescript", 19 | "ember-data", 20 | "emberjs" 21 | ], 22 | "author": "Mike North (https://mike.works)", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/mike-north/jsonapi-typescript/issues" 26 | }, 27 | "homepage": "https://github.com/mike-north/jsonapi-typescript#readme", 28 | "devDependencies": { 29 | "@commitlint/cli": "8.3.6", 30 | "@commitlint/config-conventional": "8.3.6", 31 | "@commitlint/travis-cli": "8.3.6", 32 | "@mike-north/js-lib-renovate-config": "1.3.1", 33 | "@mike-north/js-lib-semantic-release-config": "1.0.1", 34 | "dtslint": "0.9.9", 35 | "husky": "2.7.0", 36 | "nodemon": "1.19.4", 37 | "semantic-release": "15.13.3", 38 | "tslint": "5.17.0", 39 | "typescript": "3.5.3" 40 | }, 41 | "dependencies": { 42 | "json-typescript": "^1.0.0" 43 | }, 44 | "commitlint": { 45 | "extends": [ 46 | "@commitlint/config-conventional" 47 | ] 48 | }, 49 | "husky": { 50 | "hooks": { 51 | "commit-msg": "./node_modules/.bin/commitlint -e $HUSKY_GIT_PARAMS" 52 | } 53 | }, 54 | "release": { 55 | "extends": "@mike-north/js-lib-semantic-release-config" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "@mike-north/js-lib-renovate-config" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "noImplicitAny": true, 6 | "lib": [ 7 | "es2015" 8 | ], 9 | "experimentalDecorators": true, 10 | "noImplicitThis": true, 11 | "strictFunctionTypes": true, 12 | "strictNullChecks": true, 13 | "sourceMap": true, 14 | "noUnusedParameters": true, 15 | "noEmitOnError": true, 16 | "noEmit": true 17 | }, 18 | "exclude": [ 19 | "node_modules/**/*", 20 | "test/**/*" 21 | ], 22 | "include": [ 23 | "index.ts" 24 | ] 25 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "dtslint/dt.json", 3 | "rules": { 4 | // Heavy use of Function type in this older package. 5 | "ban-types": false, 6 | "jsdoc-format": false, 7 | "no-misused-new": false, 8 | 9 | // these are disabled because of rfc176 module exports 10 | "strict-export-declare-modifiers": false, 11 | "no-single-declare-module": false, 12 | "no-declare-current-package": false, 13 | "no-self-import": false, 14 | 15 | // We use interfaces in a number of places to express things (including 16 | // mixins in particular, but also including extending a global 17 | // interface) which TS currently can't express correctly. 18 | "no-empty-interface": false, 19 | 20 | "no-duplicate-imports": false, 21 | "no-unnecessary-qualifier": false, 22 | "prefer-const": false, 23 | "void-return": false, 24 | "no-void-expression": false, 25 | "only-arrow-functions": false, 26 | "no-submodule-imports": false, 27 | 28 | "no-unnecessary-class": false 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /type-tests/errors.test.ts: -------------------------------------------------------------------------------- 1 | import * as JSONAPI from '..'; 2 | 3 | const a: JSONAPI.ErrorObject = { 4 | status: '500', 5 | title: 'Server Error', 6 | detail: 'Boom!', 7 | links: { 8 | related: 'https://reactjs.com' 9 | }, 10 | meta: { 11 | dbquery: 'SELECT * FROM infinite_table' 12 | } 13 | }; 14 | 15 | const b: JSONAPI.ErrorObject = { 16 | foo: 'bar' // $ExpectError 17 | }; 18 | -------------------------------------------------------------------------------- /type-tests/index.d.ts: -------------------------------------------------------------------------------- 1 | // TypeScript Version: 2.3 2 | export * from '../index'; 3 | -------------------------------------------------------------------------------- /type-tests/links.test.ts: -------------------------------------------------------------------------------- 1 | import * as JSONAPI from '..'; 2 | 3 | let l: JSONAPI.Links; 4 | // Self-linking 5 | l = { self: 'url' }; 6 | // Related-linking 7 | l = { related: 'url' }; 8 | // Object representation of link, with meta 9 | l = { 10 | self: { 11 | href: 'url', 12 | meta: { key: 'value' } 13 | } 14 | }; 15 | 16 | // $ExpectError 17 | const o: JSONAPI.Link = {}; 18 | -------------------------------------------------------------------------------- /type-tests/meta.test.ts: -------------------------------------------------------------------------------- 1 | import * as JSONAPI from '..'; 2 | 3 | // Array case 4 | let d1: JSONAPI.DocWithData = { 5 | data: [ 6 | { 7 | type: 'foo' 8 | } 9 | ], 10 | meta: { 11 | foo: 'bar' 12 | } 13 | }; 14 | // Object case 15 | d1 = { 16 | data: { 17 | type: 'foo' 18 | }, 19 | meta: { 20 | foo: 'bar' 21 | } 22 | }; 23 | 24 | // Document only with MetaData 25 | const d: JSONAPI.DocBase = { 26 | meta: { 27 | foo: 'bar' 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /type-tests/relationships.test.ts: -------------------------------------------------------------------------------- 1 | import * as JSONAPI from '..'; 2 | 3 | const a: JSONAPI.RelationshipObject = { 4 | links: { 5 | self: 'http://example.com/articles/1/relationships/author', 6 | related: { 7 | href: 'http://example.com/articles/1/author', 8 | meta: { key: 'val' } 9 | } 10 | }, 11 | meta: { key: 'value' }, 12 | data: { type: 'people', id: '9' } 13 | }; 14 | 15 | // $ExpectError 16 | const b: JSONAPI.RelationshipObject = {}; 17 | -------------------------------------------------------------------------------- /type-tests/resources.test.ts: -------------------------------------------------------------------------------- 1 | import * as JSONAPI from '..'; 2 | 3 | const a: JSONAPI.ResourceObject = { 4 | type: 'articles', 5 | id: '1', 6 | attributes: { 7 | title: 'Rails is Omakase' 8 | }, 9 | relationships: { 10 | author: { 11 | links: { 12 | self: 'http://example.com/articles/1/relationships/author', 13 | related: 'http://example.com/articles/1/author' 14 | }, 15 | data: { type: 'people', id: '9' } 16 | } 17 | }, 18 | links: { 19 | self: 'http://example.com/articles/1' 20 | }, 21 | meta: { 22 | key: 'value' 23 | } 24 | }; 25 | 26 | // $ExpectError 27 | const b: JSONAPI.ResourceObject = {}; 28 | 29 | // $ExpectError 30 | const c: JSONAPI.ResourceObject = { 31 | id: '41' 32 | }; 33 | -------------------------------------------------------------------------------- /type-tests/toplevel-collection.test.ts: -------------------------------------------------------------------------------- 1 | import * as JSONAPI from '..'; 2 | 3 | // single resource case 4 | const d: JSONAPI.DocWithData = { 5 | data: [ 6 | { 7 | type: 'foo' 8 | } 9 | ] 10 | }; 11 | 12 | // With attributges 13 | const e: JSONAPI.CollectionResourceDoc<'book'> = { 14 | data: [ 15 | { 16 | type: 'book', 17 | attributes: { 18 | foo: 'Great Expectations', 19 | bar: 12, 20 | baz: 'F. Scott Fitzgerald' 21 | } 22 | } 23 | ] 24 | }; 25 | 26 | // specific type w/ specific attributes 27 | const f: JSONAPI.CollectionResourceDoc< 28 | 'book', 29 | { title: string; author: string; chapters: number } 30 | > = { 31 | data: [ 32 | { 33 | type: 'book', 34 | attributes: { 35 | title: 'Great Expectations', 36 | chapters: 12, 37 | author: 'F. Scott Fitzgerald' 38 | } 39 | } 40 | ] 41 | }; 42 | 43 | function badDoc1( 44 | x: JSONAPI.CollectionResourceDoc< 45 | 'book', 46 | { title: string; author: string; chapters: number } 47 | > 48 | ) {} 49 | const y = { 50 | data: [ 51 | { 52 | type: 'book', 53 | attributes: { 54 | title: 'Great Expectations', 55 | chapters: '12', 56 | author: 'F. Scott Fitzgerald' 57 | } 58 | } 59 | ] 60 | }; 61 | badDoc1(y); // $ExpectError 62 | 63 | const badDoc2: JSONAPI.CollectionResourceDoc< 64 | 'book', 65 | { title: string; author: string; chapters: number } 66 | > = { 67 | data: [ 68 | { 69 | type: 'book', 70 | attributes: { 71 | foo: 'Great Expectations', // $ExpectError 72 | bar: 12, 73 | baz: 'F. Scott Fitzgerald' 74 | } 75 | } 76 | ] 77 | }; 78 | 79 | function badDoc3(x: JSONAPI.CollectionResourceDoc<'book'>) {} 80 | const val = { 81 | data: [ 82 | { 83 | type: 'music', 84 | attributes: { 85 | foo: 'Great Expectations', 86 | bar: 12, 87 | baz: 'F. Scott Fitzgerald' 88 | } 89 | } 90 | ] 91 | }; 92 | badDoc3(val); // $ExpectError 93 | -------------------------------------------------------------------------------- /type-tests/toplevel-single.test.ts: -------------------------------------------------------------------------------- 1 | import * as JSONAPI from '..'; 2 | 3 | const d: JSONAPI.DocWithData = { 4 | data: { 5 | type: 'foo' 6 | } 7 | }; 8 | 9 | const e: JSONAPI.SingleResourceDoc<'book'> = { 10 | data: { 11 | type: 'book', 12 | attributes: { 13 | foo: 'Great Expectations', 14 | bar: 12, 15 | baz: 'F. Scott Fitzgerald' 16 | } 17 | } 18 | }; 19 | 20 | const f: JSONAPI.SingleResourceDoc< 21 | 'book', 22 | { title: string; author: string; chapters: number } 23 | > = { 24 | data: { 25 | type: 'book', 26 | attributes: { 27 | title: 'Great Expectations', 28 | chapters: 12, 29 | author: 'F. Scott Fitzgerald' 30 | } 31 | } 32 | }; 33 | 34 | const g = { 35 | data: { 36 | type: 'book', 37 | attributes: { 38 | title: 'Great Expectations', 39 | chapters: '12', 40 | author: 'F. Scott Fitzgerald' 41 | } 42 | } 43 | }; 44 | function gFunc( 45 | g: JSONAPI.SingleResourceDoc< 46 | 'book', 47 | { title: string; author: string; chapters: number } 48 | > 49 | ) {} 50 | gFunc(g); // $ExpectError 51 | 52 | const h: JSONAPI.SingleResourceDoc< 53 | 'book', 54 | { title: string; author: string; chapters: number } 55 | > = { 56 | data: { 57 | type: 'book', 58 | attributes: { 59 | foo: 'Great Expectations', // $ExpectError 60 | bar: 12, 61 | baz: 'F. Scott Fitzgerald' 62 | } 63 | } 64 | }; 65 | 66 | const i = { 67 | data: { 68 | type: 'music', 69 | attributes: { 70 | foo: 'Great Expectations', 71 | bar: 12, 72 | baz: 'F. Scott Fitzgerald' 73 | } 74 | } 75 | }; 76 | function iFunc(x: JSONAPI.SingleResourceDoc<'book'>) {} 77 | iFunc(i); // $ExpectError 78 | -------------------------------------------------------------------------------- /type-tests/toplevel.test.ts: -------------------------------------------------------------------------------- 1 | import * as JSONAPI from '..'; 2 | 3 | let doc: JSONAPI.Document; 4 | // Only errors 5 | doc = { errors: [] }; 6 | // Only data 7 | doc = { 8 | data: { 9 | type: 'articles', 10 | id: '1' 11 | } 12 | }; 13 | // Only meta 14 | doc = { meta: { something: { nested: 'here ' } } }; 15 | // Optional links and included 16 | doc = { 17 | data: [], 18 | links: { 19 | related: { 20 | href: 'http://example.com/articles/1/comments', 21 | meta: { 22 | count: 10 23 | } 24 | } 25 | } 26 | }; 27 | // Add optional ImplementationInfo 28 | doc = { errors: [], jsonapi: {} }; 29 | // ImplementationInfo.Version and ImplementationInfo.Meta are Optional 30 | doc = { errors: [], jsonapi: { version: '0.2' } }; 31 | // Free-for all (JSON) in meta 32 | doc = { errors: [], jsonapi: { meta: { something: 'here ' } } }; 33 | doc = { errors: [], jsonapi: { meta: { something: { nested: 'here ' } } } }; 34 | 35 | doc = { 36 | data: [ 37 | { 38 | type: 'articles', 39 | id: '1', 40 | attributes: { 41 | title: 'JSON API paints my bikeshed!' 42 | }, 43 | links: { 44 | self: 'http://example.com/articles/1' 45 | }, 46 | relationships: { 47 | author: { 48 | links: { 49 | self: 'http://example.com/articles/1/relationships/author', 50 | related: 'http://example.com/articles/1/author' 51 | }, 52 | data: { type: 'people', id: '9' } 53 | }, 54 | comments: { 55 | links: { 56 | self: 'http://example.com/articles/1/relationships/comments', 57 | related: 'http://example.com/articles/1/comments' 58 | }, 59 | data: [{ type: 'comments', id: '5' }, { type: 'comments', id: '12' }] 60 | } 61 | } 62 | } 63 | ], 64 | included: [ 65 | { 66 | type: 'people', 67 | id: '9', 68 | attributes: { 69 | 'first-name': 'Dan', 70 | 'last-name': 'Gebhardt', 71 | twitter: 'dgeb' 72 | }, 73 | links: { 74 | self: 'http://example.com/people/9' 75 | } 76 | }, 77 | { 78 | type: 'comments', 79 | id: '5', 80 | attributes: { 81 | body: 'First!' 82 | }, 83 | relationships: { 84 | author: { 85 | data: { type: 'people', id: '2' } 86 | } 87 | }, 88 | links: { 89 | self: 'http://example.com/comments/5' 90 | } 91 | }, 92 | { 93 | type: 'comments', 94 | id: '12', 95 | attributes: { 96 | body: 'I like XML better' 97 | }, 98 | relationships: { 99 | author: { 100 | data: { type: 'people', id: '9' } 101 | } 102 | }, 103 | links: { 104 | self: 'http://example.com/comments/12' 105 | } 106 | } 107 | ] 108 | }; 109 | 110 | // Array case 111 | let doc2: JSONAPI.DocWithData = { 112 | data: [ 113 | { 114 | type: 'foo' 115 | } 116 | ] 117 | }; 118 | // Object case 119 | doc2 = { 120 | data: { 121 | type: 'foo' 122 | } 123 | }; 124 | 125 | const badDoc1: JSONAPI.DocWithData = { 126 | data: { 127 | type: 'foo' // $ExpectError 128 | } 129 | }; 130 | 131 | const badDoc2 = { 132 | jsonapi: { meta: '5' }, 133 | errors: [] as JSONAPI.Errors 134 | }; 135 | function badDoc2Test(x: JSONAPI.Document) {} 136 | badDoc2Test(badDoc2); // $ExpectError 137 | 138 | const badDoc3 = { 139 | data: [ 140 | { 141 | type: 'foo' 142 | } 143 | ] 144 | }; 145 | function badDoc3Test(x: JSONAPI.DocWithData) {} 146 | badDoc3Test(badDoc3); // $ExpectError 147 | 148 | const badDoc4: JSONAPI.Document = true; // $ExpectError 149 | // $ExpectError 150 | const badDoc5: JSONAPI.Document = { 151 | jsonapi: { version: '1.0' } 152 | }; 153 | const badDoc6: JSONAPI.Document = 'abc'; // $ExpectError 154 | const badDoc7: JSONAPI.Document = 4; // $ExpectError 155 | -------------------------------------------------------------------------------- /type-tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "lib": ["es6"], 5 | "noImplicitAny": true, 6 | "noImplicitThis": true, 7 | "strictNullChecks": true, 8 | "strictFunctionTypes": true, 9 | "noEmit": true, 10 | 11 | // If the library is an external module (uses `export`), this allows your test file to import "mylib" instead of "./index". 12 | // If the library is global (cannot be imported via `import` or `require`), leave this out. 13 | "baseUrl": ".", 14 | "paths": { "jsonapi-typescript": [".."] } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /type-tests/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "dtslint/dtslint.json", // Or "dtslint/dt.json" if on DefinitelyTyped 3 | "rules": { 4 | "semicolon": false, 5 | "indent": [true, "tabs"], 6 | "whitespace": false 7 | } 8 | } 9 | --------------------------------------------------------------------------------