Browse Source

Actually check types (#1918)

* types: stronger typings

* Remove remaining `declare module` things for uppy.use

* Format types standard-style-like

* Add `target` properties to plugin typings

* Add type for `replaceTargetContent` option

* xhr-upload: allow lowercase method

* opt in to stricter typechecks

* use the strictly typed version in all typings tests

* endtoend: use strict types in typescript test

* Add some comments to the typings file so IDEs will warn about untyped use()

* informer: remove obsolete option from typings

* react: generate prop types based on actual options types

* react: update types

* react: fix import in typings test

* companion-client: add `pluginId` property to ProviderOptions type

Co-Authored-By: Ifedapo .A. Olarewaju <ifedapoolarewaju@gmail.com>

* core: remove some `any` types

* *: add locale string types

* *: generate locale typings

* core: fix LocaleStrings<> type

* dashboard: add locale string type tests

* tus: inherit options typings from tus-js-client

* transloadit: add missing options types

* ci: do the required build steps before testing typings

* Support TokenStorage in types, add `title?: string` options

* form: update typings

* types: put TokenStorage type in dependents

* Document Uppy.StrictTypes

* if we pin at least we should use the latest

* Allow document.querySelector

I think it's a eslint-plugin-compat bug that it's marking querySelector
as not available in Android Chrome 78.

Co-authored-by: Ifedapo .A. Olarewaju <ifedapoolarewaju@gmail.com>
Renée Kooi 5 years ago
parent
commit
be66c47deb
83 changed files with 2135 additions and 1166 deletions
  1. 2 1
      .eslintrc.json
  2. 1 0
      .gitignore
  3. 2 1
      .travis.yml
  4. 24 0
      bin/locale-packs.js
  5. 869 87
      package-lock.json
  6. 64 63
      package.json
  7. 0 34
      packages/@uppy/aws-s3-multipart/types/aws-s3-multipart-tests.ts
  8. 32 22
      packages/@uppy/aws-s3-multipart/types/index.d.ts
  9. 39 0
      packages/@uppy/aws-s3-multipart/types/index.test-d.ts
  10. 0 11
      packages/@uppy/aws-s3/types/aws-s3-tests.ts
  11. 15 19
      packages/@uppy/aws-s3/types/index.d.ts
  12. 13 0
      packages/@uppy/aws-s3/types/index.test-d.ts
  13. 51 29
      packages/@uppy/companion-client/types/index.d.ts
  14. 2 0
      packages/@uppy/companion-client/types/index.test-d.ts
  15. 0 71
      packages/@uppy/core/types/core-tests.ts
  16. 203 113
      packages/@uppy/core/types/index.d.ts
  17. 91 0
      packages/@uppy/core/types/index.test-d.ts
  18. 0 32
      packages/@uppy/dashboard/types/dashboard-tests.ts
  19. 45 102
      packages/@uppy/dashboard/types/index.d.ts
  20. 64 0
      packages/@uppy/dashboard/types/index.test-d.ts
  21. 12 16
      packages/@uppy/drag-drop/types/index.d.ts
  22. 2 0
      packages/@uppy/drag-drop/types/index.test-d.ts
  23. 11 15
      packages/@uppy/dropbox/types/index.d.ts
  24. 2 0
      packages/@uppy/dropbox/types/index.test-d.ts
  25. 11 15
      packages/@uppy/facebook/types/index.d.ts
  26. 2 0
      packages/@uppy/facebook/types/index.test-d.ts
  27. 10 14
      packages/@uppy/file-input/types/index.d.ts
  28. 1 0
      packages/@uppy/file-input/types/index.test-d.ts
  29. 1 1
      packages/@uppy/form/src/index.js
  30. 11 16
      packages/@uppy/form/types/index.d.ts
  31. 1 0
      packages/@uppy/form/types/index.test-d.ts
  32. 8 14
      packages/@uppy/golden-retriever/types/index.d.ts
  33. 1 0
      packages/@uppy/golden-retriever/types/index.test-d.ts
  34. 11 15
      packages/@uppy/google-drive/types/index.d.ts
  35. 9 0
      packages/@uppy/google-drive/types/index.test-d.ts
  36. 5 19
      packages/@uppy/informer/types/index.d.ts
  37. 1 0
      packages/@uppy/informer/types/index.test-d.ts
  38. 11 15
      packages/@uppy/instagram/types/index.d.ts
  39. 2 0
      packages/@uppy/instagram/types/index.test-d.ts
  40. 11 15
      packages/@uppy/onedrive/types/index.d.ts
  41. 2 0
      packages/@uppy/onedrive/types/index.test-d.ts
  42. 7 13
      packages/@uppy/progress-bar/types/index.d.ts
  43. 2 0
      packages/@uppy/progress-bar/types/index.test-d.ts
  44. 11 2
      packages/@uppy/react/src/CommonTypes.d.ts
  45. 13 32
      packages/@uppy/react/src/Dashboard.d.ts
  46. 5 10
      packages/@uppy/react/src/DashboardModal.d.ts
  47. 5 7
      packages/@uppy/react/src/DragDrop.d.ts
  48. 5 8
      packages/@uppy/react/src/ProgressBar.d.ts
  49. 5 8
      packages/@uppy/react/src/StatusBar.d.ts
  50. 6 6
      packages/@uppy/react/types/index.d.ts
  51. 20 0
      packages/@uppy/react/types/index.test-d.tsx
  52. 6 13
      packages/@uppy/redux-dev-tools/types/index.d.ts
  53. 1 0
      packages/@uppy/redux-dev-tools/types/index.test-d.ts
  54. 12 14
      packages/@uppy/status-bar/types/index.d.ts
  55. 2 0
      packages/@uppy/status-bar/types/index.test-d.ts
  56. 7 7
      packages/@uppy/store-default/types/index.d.ts
  57. 6 0
      packages/@uppy/store-default/types/index.test-d.ts
  58. 0 6
      packages/@uppy/store-default/types/store-default-tests.ts
  59. 16 14
      packages/@uppy/store-redux/types/index.d.ts
  60. 13 0
      packages/@uppy/store-redux/types/index.test-d.ts
  61. 0 13
      packages/@uppy/store-redux/types/store-redux-tests.ts
  62. 8 12
      packages/@uppy/thumbnail-generator/types/index.d.ts
  63. 2 0
      packages/@uppy/thumbnail-generator/types/index.test-d.ts
  64. 30 27
      packages/@uppy/transloadit/types/index.d.ts
  65. 63 0
      packages/@uppy/transloadit/types/index.test-d.ts
  66. 0 53
      packages/@uppy/transloadit/types/transloadit-tests.ts
  67. 1 0
      packages/@uppy/tus/package.json
  68. 19 23
      packages/@uppy/tus/types/index.d.ts
  69. 1 0
      packages/@uppy/tus/types/index.test-d.ts
  70. 12 14
      packages/@uppy/url/types/index.d.ts
  71. 2 0
      packages/@uppy/url/types/index.test-d.ts
  72. 117 85
      packages/@uppy/utils/types/index.d.ts
  73. 1 0
      packages/@uppy/utils/types/index.test-d.ts
  74. 18 17
      packages/@uppy/webcam/types/index.d.ts
  75. 8 0
      packages/@uppy/webcam/types/index.test-d.ts
  76. 0 8
      packages/@uppy/webcam/types/webcam-tests.ts
  77. 15 21
      packages/@uppy/xhr-upload/types/index.d.ts
  78. 19 0
      packages/@uppy/xhr-upload/types/index.test-d.ts
  79. 0 9
      packages/@uppy/xhr-upload/types/xhr-upload-tests.ts
  80. 0 0
      packages/uppy/types/index.test-d.ts
  81. 1 1
      test/endtoend/typescript/main.ts
  82. 1 3
      tsconfig.json
  83. 33 0
      website/src/docs/uppy.md

+ 2 - 1
.eslintrc.json

@@ -33,7 +33,8 @@
     "polyfills": [
     "polyfills": [
       "Promise",
       "Promise",
       "fetch",
       "fetch",
-      "Object.assign"
+      "Object.assign",
+      "document.querySelector"
     ]
     ]
   },
   },
   "overrides": [{
   "overrides": [{

+ 1 - 0
.gitignore

@@ -16,6 +16,7 @@ examples/dev/bundle.js
 test/endtoend/create-react-app/build/
 test/endtoend/create-react-app/build/
 test/endtoend/create-react-app/coverage/
 test/endtoend/create-react-app/coverage/
 uppy-*.tgz
 uppy-*.tgz
+generatedLocale.d.ts
 
 
 website/db.json
 website/db.json
 website/*.log
 website/*.log

+ 2 - 1
.travis.yml

@@ -67,7 +67,8 @@ install:
 - npm ci
 - npm ci
 script:
 script:
 - 'if [ -n "${LINT-}" ]; then npm run lint; fi'
 - 'if [ -n "${LINT-}" ]; then npm run lint; fi'
-- 'if [ -n "${LINT-}" ]; then npm run test:type; fi'
+# Need to do a bunch of work to generate the locale typings 🙃
+- 'if [ -n "${LINT-}" ]; then npm run build:lib && npm run build:companion && npm run build:locale-pack && npm run test:type; fi'
 - 'if [ -n "${UNIT-}" ]; then npm run test:unit; fi'
 - 'if [ -n "${UNIT-}" ]; then npm run test:unit; fi'
 - 'if [ -n "${COMPANION_NODE6-}" ]; then nvm install 6.0.0 && nvm use 6.0.0; fi'
 - 'if [ -n "${COMPANION_NODE6-}" ]; then nvm install 6.0.0 && nvm use 6.0.0; fi'
 - 'if [ -n "${COMPANION-}" ] || [ -n "${COMPANION_NODE6-}" ]; then npm run test:companion; fi'
 - 'if [ -n "${COMPANION-}" ] || [ -n "${COMPANION_NODE6-}" ]; then npm run test:companion; fi'

+ 24 - 0
bin/locale-packs.js

@@ -154,6 +154,26 @@ function sortObjectAlphabetically (obj, sortFunc) {
   }, {})
   }, {})
 }
 }
 
 
+function createTypeScriptLocale (plugin, pluginName) {
+  const allowedStringTypes = Object.keys(plugin.defaultLocale.strings)
+    .map(key => `  | '${key}'`)
+    .join('\n')
+
+  const pluginClassName = pluginName === 'core' ? 'Core' : plugin.id
+  const localePath = path.join(__dirname, '..', 'packages', '@uppy', pluginName, 'types', 'generatedLocale.d.ts')
+
+  const localeTypes =
+    'import Uppy = require(\'@uppy/core\')\n' +
+    '\n' +
+    `type ${pluginClassName}Locale = Uppy.Locale` + '<\n' +
+    allowedStringTypes + '\n' +
+    '>\n' +
+    '\n' +
+    `export = ${pluginClassName}Locale\n`
+
+  fs.writeFileSync(localePath, localeTypes)
+}
+
 function build () {
 function build () {
   const { plugins, sources } = buildPluginsList()
   const { plugins, sources } = buildPluginsList()
 
 
@@ -161,6 +181,10 @@ function build () {
     addLocaleToPack(plugins[pluginName], pluginName)
     addLocaleToPack(plugins[pluginName], pluginName)
   }
   }
 
 
+  for (const pluginName in plugins) {
+    createTypeScriptLocale(plugins[pluginName], pluginName)
+  }
+
   localePack = sortObjectAlphabetically(localePack)
   localePack = sortObjectAlphabetically(localePack)
 
 
   for (const pluginName in sources) {
   for (const pluginName in sources) {

File diff suppressed because it is too large
+ 869 - 87
package-lock.json


+ 64 - 63
package.json

@@ -69,69 +69,69 @@
     "uppy.io": "file:website"
     "uppy.io": "file:website"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@babel/cli": "7.7.5",
-    "@babel/core": "7.7.5",
-    "@babel/plugin-proposal-class-properties": "7.7.4",
-    "@babel/plugin-transform-object-assign": "7.7.4",
-    "@babel/plugin-transform-proto-to-assign": "7.7.4",
-    "@babel/plugin-transform-react-jsx": "7.7.4",
-    "@babel/polyfill": "7.4.4",
-    "@babel/preset-env": "7.7.6",
-    "@babel/register": "7.7.4",
+    "@babel/cli": "7.8.4",
+    "@babel/core": "7.8.4",
+    "@babel/plugin-proposal-class-properties": "7.8.3",
+    "@babel/plugin-transform-object-assign": "7.8.3",
+    "@babel/plugin-transform-proto-to-assign": "7.8.3",
+    "@babel/plugin-transform-react-jsx": "7.8.3",
+    "@babel/polyfill": "7.8.3",
+    "@babel/preset-env": "7.8.4",
+    "@babel/register": "7.8.3",
     "@jamen/lorem": "0.2.0",
     "@jamen/lorem": "0.2.0",
     "@lerna/run": "3.20.0",
     "@lerna/run": "3.20.0",
-    "@octokit/rest": "16.25.0",
-    "@size-limit/preset-big-lib": "2.2.2",
-    "@types/aws-serverless-express": "3.0.1",
+    "@octokit/rest": "16.43.1",
+    "@size-limit/preset-big-lib": "2.2.4",
+    "@types/aws-serverless-express": "3.3.3",
     "@types/compression": "0.0.36",
     "@types/compression": "0.0.36",
     "@types/connect-redis": "0.0.7",
     "@types/connect-redis": "0.0.7",
-    "@types/cookie-parser": "1.4.1",
-    "@types/cors": "2.8.3",
-    "@types/express-session": "1.15.6",
+    "@types/cookie-parser": "1.4.2",
+    "@types/cors": "2.8.6",
+    "@types/express-session": "1.15.16",
     "@types/helmet": "0.0.37",
     "@types/helmet": "0.0.37",
-    "@types/jsonwebtoken": "7.2.5",
-    "@types/lodash.merge": "4.6.3",
-    "@types/morgan": "1.7.35",
-    "@types/ms": "0.7.30",
-    "@types/node": "12.12.17",
-    "@types/react": "16.9.16",
-    "@types/request": "2.0.9",
+    "@types/jsonwebtoken": "7.2.8",
+    "@types/lodash.merge": "4.6.6",
+    "@types/morgan": "1.7.37",
+    "@types/ms": "0.7.31",
+    "@types/node": "12.12.27",
+    "@types/react": "16.9.19",
+    "@types/request": "2.48.4",
     "@types/tus-js-client": "1.8.0",
     "@types/tus-js-client": "1.8.0",
-    "@types/uuid": "3.4.3",
+    "@types/uuid": "3.4.7",
     "@types/ws": "3.2.1",
     "@types/ws": "3.2.1",
-    "@wdio/cli": "5.16.15",
-    "@wdio/local-runner": "5.16.15",
-    "@wdio/mocha-framework": "5.16.15",
+    "@wdio/cli": "5.18.6",
+    "@wdio/local-runner": "5.18.6",
+    "@wdio/mocha-framework": "5.18.6",
     "@wdio/sauce-service": "5.16.10",
     "@wdio/sauce-service": "5.16.10",
     "adm-zip": "0.4.14",
     "adm-zip": "0.4.14",
     "aliasify": "2.1.0",
     "aliasify": "2.1.0",
-    "autoprefixer": "9.7.3",
+    "autoprefixer": "9.7.4",
     "babel-eslint": "10.0.3",
     "babel-eslint": "10.0.3",
     "babel-jest": "24.9.0",
     "babel-jest": "24.9.0",
     "babel-plugin-inline-package-json": "2.0.0",
     "babel-plugin-inline-package-json": "2.0.0",
     "babelify": "10.0.0",
     "babelify": "10.0.0",
     "brake": "1.0.1",
     "brake": "1.0.1",
     "browser-resolve": "1.11.3",
     "browser-resolve": "1.11.3",
-    "browser-sync": "2.26.5",
-    "browserify": "16.2.3",
+    "browser-sync": "2.26.7",
+    "browserify": "16.5.0",
     "chai": "4.2.0",
     "chai": "4.2.0",
     "chalk": "2.4.2",
     "chalk": "2.4.2",
     "cssnano": "4.1.10",
     "cssnano": "4.1.10",
     "disc": "1.3.3",
     "disc": "1.3.3",
     "envify": "4.1.0",
     "envify": "4.1.0",
-    "enzyme": "3.9.0",
-    "enzyme-adapter-react-16": "1.11.2",
-    "es6-promise": "4.2.5",
-    "eslint": "6.7.2",
+    "enzyme": "3.11.0",
+    "enzyme-adapter-react-16": "1.15.2",
+    "es6-promise": "4.2.8",
+    "eslint": "6.8.0",
     "eslint-config-standard": "14.1.0",
     "eslint-config-standard": "14.1.0",
     "eslint-config-standard-jsx": "8.1.0",
     "eslint-config-standard-jsx": "8.1.0",
-    "eslint-plugin-compat": "3.3.0",
-    "eslint-plugin-import": "2.19.1",
-    "eslint-plugin-jest": "22.17.0",
-    "eslint-plugin-jsdoc": "15.5.3",
+    "eslint-plugin-compat": "3.5.1",
+    "eslint-plugin-import": "2.20.1",
+    "eslint-plugin-jest": "22.21.0",
+    "eslint-plugin-jsdoc": "15.12.2",
     "eslint-plugin-node": "10.0.0",
     "eslint-plugin-node": "10.0.0",
     "eslint-plugin-promise": "4.2.1",
     "eslint-plugin-promise": "4.2.1",
-    "eslint-plugin-react": "7.17.0",
+    "eslint-plugin-react": "7.18.3",
     "eslint-plugin-standard": "4.0.1",
     "eslint-plugin-standard": "4.0.1",
     "events.once": "2.0.2",
     "events.once": "2.0.2",
     "exorcist": "1.0.1",
     "exorcist": "1.0.1",
@@ -139,51 +139,52 @@
     "fakefile": "0.0.9",
     "fakefile": "0.0.9",
     "flat": "4.1.0",
     "flat": "4.1.0",
     "github-contributors-list": "1.2.4",
     "github-contributors-list": "1.2.4",
-    "glob": "7.1.3",
+    "glob": "7.1.6",
     "globby": "9.2.0",
     "globby": "9.2.0",
-    "gzip-size": "5.0.0",
+    "gzip-size": "5.1.1",
     "http-proxy": "1.18.0",
     "http-proxy": "1.18.0",
     "isomorphic-fetch": "2.2.1",
     "isomorphic-fetch": "2.2.1",
     "jest": "24.9.0",
     "jest": "24.9.0",
-    "json3": "3.3.2",
+    "json3": "3.3.3",
     "last-commit-message": "1.0.0",
     "last-commit-message": "1.0.0",
-    "lerna": "3.17.0",
+    "lerna": "3.20.2",
     "lint-staged": "9.5.0",
     "lint-staged": "9.5.0",
-    "mime-types": "2.1.24",
-    "minify-stream": "1.2.0",
+    "mime-types": "2.1.26",
+    "minify-stream": "1.2.1",
     "mkdirp": "0.5.1",
     "mkdirp": "0.5.1",
     "multi-glob": "1.0.2",
     "multi-glob": "1.0.2",
     "namespace-emitter": "2.0.1",
     "namespace-emitter": "2.0.1",
     "nock": "9.6.1",
     "nock": "9.6.1",
     "node-sass": "4.13.1",
     "node-sass": "4.13.1",
-    "nodemon": "1.17.5",
+    "nodemon": "1.19.4",
     "npm-auth-to-token": "1.0.0",
     "npm-auth-to-token": "1.0.0",
-    "npm-packlist": "1.4.7",
+    "npm-packlist": "1.4.8",
     "npm-run-all": "4.1.5",
     "npm-run-all": "4.1.5",
     "onchange": "4.1.0",
     "onchange": "4.1.0",
-    "pacote": "9.5.11",
-    "postcss": "7.0.24",
-    "postcss-safe-important": "1.1.0",
+    "pacote": "9.5.12",
+    "postcss": "7.0.26",
+    "postcss-safe-important": "1.2.0",
     "pre-commit": "1.2.2",
     "pre-commit": "1.2.2",
-    "pretty-bytes": "5.1.0",
-    "react": "16.8.6",
-    "react-dom": "16.8.6",
-    "redux": "4.0.1",
-    "replace-x": "1.5.0",
-    "rimraf": "2.6.3",
+    "pretty-bytes": "5.3.0",
+    "react": "16.12.0",
+    "react-dom": "16.12.0",
+    "redux": "4.0.5",
+    "replace-x": "1.7.2",
+    "rimraf": "2.7.1",
     "stringify-object": "3.3.0",
     "stringify-object": "3.3.0",
-    "supertest": "3.0.0",
-    "tar": "4.4.8",
+    "supertest": "3.4.2",
+    "tar": "4.4.13",
     "temp-write": "3.4.0",
     "temp-write": "3.4.0",
-    "tinyify": "2.5.0",
+    "tinyify": "2.5.2",
     "touch": "3.1.0",
     "touch": "3.1.0",
+    "tsd": "0.10.0",
     "tsify": "4.0.1",
     "tsify": "4.0.1",
     "tus-node-server": "0.3.2",
     "tus-node-server": "0.3.2",
-    "typescript": "3.7.3",
-    "verdaccio": "4.2.1",
+    "typescript": "3.7.5",
+    "verdaccio": "4.4.2",
     "watchify": "3.11.1",
     "watchify": "3.11.1",
-    "webdriverio": "5.16.15",
-    "webpack": "4.41.1",
+    "webdriverio": "5.18.6",
+    "webpack": "4.41.6",
     "whatwg-fetch": "3.0.0"
     "whatwg-fetch": "3.0.0"
   },
   },
   "scripts": {
   "scripts": {
@@ -219,7 +220,7 @@
     "test:endtoend:registry": "verdaccio --listen 4002 --config test/endtoend/verdaccio.yaml",
     "test:endtoend:registry": "verdaccio --listen 4002 --config test/endtoend/verdaccio.yaml",
     "test:endtoend": "npm run test:endtoend:prepare-ci && wdio test/endtoend/wdio.remote.conf.js",
     "test:endtoend": "npm run test:endtoend:prepare-ci && wdio test/endtoend/wdio.remote.conf.js",
     "test:locale-packs": "node ./bin/locale-packs.js test",
     "test:locale-packs": "node ./bin/locale-packs.js test",
-    "test:type": "tsc -p .",
+    "test:type": "lerna exec --scope '@uppy/*' --ignore '@uppy/{react-native,locales,companion,provider-views,robodog}' tsd",
     "test:unit": "npm run build:lib && jest",
     "test:unit": "npm run build:lib && jest",
     "test:watch": "jest --watch",
     "test:watch": "jest --watch",
     "test:size": "size-limit",
     "test:size": "size-limit",

+ 0 - 34
packages/@uppy/aws-s3-multipart/types/aws-s3-multipart-tests.ts

@@ -1,34 +0,0 @@
-import Uppy = require('@uppy/core');
-import AwsS3Multipart = require('../');
-
-{
-  const uppy = Uppy();
-  uppy.use(AwsS3Multipart, {
-    createMultipartUpload(file) {
-      file // $ExpectType Uppy.UppyFile
-    },
-    listParts(file, opts) {
-      file // $ExpectType Uppy.UppyFile
-      opts.uploadId // $ExpectType string
-      opts.key // $ExpectType string
-    },
-    prepareUploadPart(file, part) {
-      file // $ExpectType Uppy.UppyFile
-      part.uploadId // $ExpectType string
-      part.key // $ExpectType string
-      part.body // $ExpectType Blob
-      part.number // $ExpectType number
-    },
-    abortMultipartUpload(file, opts) {
-      file // $ExpectType Uppy.UppyFile
-      opts.uploadId // $ExpectType string
-      opts.key // $ExpectType string
-    },
-    completeMultipartUpload(file, opts) {
-      file // $ExpectType Uppy.UppyFile
-      opts.uploadId // $ExpectType string
-      opts.key // $ExpectType string
-      opts.parts[0] // $ExpectType AwsS3Multipart.AwsS3Part
-    },
-  });
-}

+ 32 - 22
packages/@uppy/aws-s3-multipart/types/index.d.ts

@@ -1,32 +1,42 @@
-import Uppy = require('@uppy/core');
+import Uppy = require('@uppy/core')
+
+type MaybePromise<T> = T | Promise<T>
 
 
 declare module AwsS3Multipart {
 declare module AwsS3Multipart {
   interface AwsS3Part {
   interface AwsS3Part {
-    PartNumber?: number;
-    Size?: number;
-    ETag?: string;
+    PartNumber?: number
+    Size?: number
+    ETag?: string
   }
   }
 
 
   interface AwsS3MultipartOptions extends Uppy.PluginOptions {
   interface AwsS3MultipartOptions extends Uppy.PluginOptions {
-    companionUrl: string;
-    createMultipartUpload(file: Uppy.UppyFile): Promise<{ uploadId: string, key: string }>;
-    listParts(file: Uppy.UppyFile, opts: { uploadId: string, key: string }): Promise<AwsS3Part[]>;
-    prepareUploadPart(file: Uppy.UppyFile, partData: { uploadId: string, key: string, body: Blob, number: number }): Promise<{ url: string, headers?: {[k: string]: string}}>;
-    abortMultipartUpload(file: Uppy.UppyFile, opts: { uploadId: string, key: string }): Promise<void>;
-    completeMultipartUpload(file: Uppy.UppyFile, opts: { uploadId: string, key: string, parts: AwsS3Part[] }): Promise<{ location?: string }>;
-    timeout: number;
-    limit: number;
+    companionUrl?: string
+    createMultipartUpload?: (
+      file: Uppy.UppyFile
+    ) => MaybePromise<{ uploadId: string; key: string }>
+    listParts?: (
+      file: Uppy.UppyFile,
+      opts: { uploadId: string; key: string }
+    ) => MaybePromise<AwsS3Part[]>
+    prepareUploadPart?: (
+      file: Uppy.UppyFile,
+      partData: { uploadId: string; key: string; body: Blob; number: number }
+    ) => MaybePromise<{ url: string, headers?: { [k: string]: string } }>
+    abortMultipartUpload?: (
+      file: Uppy.UppyFile,
+      opts: { uploadId: string; key: string }
+    ) => MaybePromise<void>
+    completeMultipartUpload?: (
+      file: Uppy.UppyFile,
+      opts: { uploadId: string; key: string; parts: AwsS3Part[] }
+    ) => MaybePromise<{ location?: string }>
+    timeout?: number
+    limit?: number
   }
   }
 }
 }
 
 
-declare class AwsS3Multipart extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<AwsS3Multipart.AwsS3MultipartOptions>);
-}
-
-export = AwsS3Multipart;
+declare class AwsS3Multipart extends Uppy.Plugin<
+  AwsS3Multipart.AwsS3MultipartOptions
+> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof AwsS3Multipart, opts: Partial<AwsS3Multipart.AwsS3MultipartOptions>): Uppy.Uppy;
-  }
-}
+export = AwsS3Multipart

+ 39 - 0
packages/@uppy/aws-s3-multipart/types/index.test-d.ts

@@ -0,0 +1,39 @@
+import { expectType } from 'tsd'
+import Uppy = require('@uppy/core')
+import AwsS3Multipart = require('../')
+
+{
+  const uppy = Uppy<Uppy.StrictTypes>()
+  uppy.use(AwsS3Multipart, {
+    createMultipartUpload (file) {
+      expectType<Uppy.UppyFile>(file)
+      return { uploadId: '', key: '' }
+    },
+    listParts (file, opts) {
+      expectType<Uppy.UppyFile>(file)
+      expectType<string>(opts.uploadId)
+      expectType<string>(opts.key)
+      return []
+    },
+    prepareUploadPart (file, part) {
+      expectType<Uppy.UppyFile>(file)
+      expectType<string>(part.uploadId)
+      expectType<string>(part.key)
+      expectType<Blob>(part.body)
+      expectType<number>(part.number)
+      return { url: '' }
+    },
+    abortMultipartUpload (file, opts) {
+      expectType<Uppy.UppyFile>(file)
+      expectType<string>(opts.uploadId)
+      expectType<string>(opts.key)
+    },
+    completeMultipartUpload (file, opts) {
+      expectType<Uppy.UppyFile>(file)
+      expectType<string>(opts.uploadId)
+      expectType<string>(opts.key)
+      expectType<AwsS3Multipart.AwsS3Part>(opts.parts[0])
+      return {}
+    }
+  })
+}

+ 0 - 11
packages/@uppy/aws-s3/types/aws-s3-tests.ts

@@ -1,11 +0,0 @@
-import Uppy = require('@uppy/core');
-import AwsS3 = require('../');
-
-{
-  const uppy = Uppy();
-  uppy.use(AwsS3, {
-    getUploadParameters(file) {
-      file // $ExpectType Uppy.UppyFile
-    }
-  });
-}

+ 15 - 19
packages/@uppy/aws-s3/types/index.d.ts

@@ -1,29 +1,25 @@
-import Uppy = require('@uppy/core');
+import Uppy = require('@uppy/core')
+
+type MaybePromise<T> = T | Promise<T>
 
 
 declare module AwsS3 {
 declare module AwsS3 {
   interface AwsS3UploadParameters {
   interface AwsS3UploadParameters {
-    method?: string;
-    url: string;
-    fields?: { [type: string]: string };
-    headers?: { [type: string]: string };
+    method?: string
+    url: string
+    fields?: { [type: string]: string }
+    headers?: { [type: string]: string }
   }
   }
 
 
   interface AwsS3Options extends Uppy.PluginOptions {
   interface AwsS3Options extends Uppy.PluginOptions {
-    companionUrl: string;
-    getUploadParameters(file: Uppy.UppyFile): Promise<AwsS3UploadParameters>;
-    timeout: number;
-    limit: number;
+    companionUrl?: string
+    getUploadParameters?: (
+      file: Uppy.UppyFile
+    ) => MaybePromise<AwsS3UploadParameters>
+    timeout?: number
+    limit?: number
   }
   }
 }
 }
 
 
-declare class AwsS3 extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<AwsS3.AwsS3Options>);
-}
-
-export = AwsS3;
+declare class AwsS3 extends Uppy.Plugin<AwsS3.AwsS3Options> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof AwsS3, opts: Partial<AwsS3.AwsS3Options>): Uppy.Uppy;
-  }
-}
+export = AwsS3

+ 13 - 0
packages/@uppy/aws-s3/types/index.test-d.ts

@@ -0,0 +1,13 @@
+import { expectType } from 'tsd'
+import Uppy = require('@uppy/core')
+import AwsS3 = require('../')
+
+{
+  const uppy = Uppy<Uppy.StrictTypes>()
+  uppy.use(AwsS3, {
+    getUploadParameters (file) {
+      expectType<Uppy.UppyFile>(file)
+      return { url: '' }
+    }
+  })
+}

+ 51 - 29
packages/@uppy/companion-client/types/index.d.ts

@@ -1,48 +1,70 @@
+/**
+ * Async storage interface, similar to `localStorage`. This can be used to
+ * implement custom storages for authentication tokens.
+ */
+export interface TokenStorage {
+  setItem: (key: string, value: string) => Promise<void>
+  getItem: (key: string) => Promise<string>
+  removeItem: (key: string) => Promise<void>
+}
+
 export interface RequestClientOptions {
 export interface RequestClientOptions {
-  companionUrl: string;
-  companionHeaders?: object;
+  companionUrl: string
+  companionHeaders?: object
   /**
   /**
    * Deprecated, use `companionHeaders` instead.
    * Deprecated, use `companionHeaders` instead.
    */
    */
-  serverHeaders?: object;
+  serverHeaders?: object
 }
 }
 
 
 export class RequestClient {
 export class RequestClient {
-  constructor (uppy: any, opts: RequestClientOptions);
-  get (path: string): Promise<any>;
-  post (path: string, data: object): Promise<any>;
-  delete (path: string, data: object): Promise<any>;
+  constructor (uppy: any, opts: RequestClientOptions)
+  get (path: string): Promise<any>
+  post (path: string, data: object): Promise<any>
+  delete (path: string, data: object): Promise<any>
 }
 }
 
 
-export interface ProviderOptions extends RequestClientOptions {
-  provider: string;
-  authProvider?: string;
-  name?: string;
+/**
+ * Options for Providers that can be passed in by Uppy users through
+ * Plugin constructors.
+ */
+export interface PublicProviderOptions extends RequestClientOptions {
+  companionAllowedHosts?: string | RegExp | Array<string | RegExp>
+}
+
+/**
+ * Options for Providers, including internal options that Plugins can set.
+ */
+export interface ProviderOptions extends PublicProviderOptions {
+  provider: string
+  authProvider?: string
+  name?: string
+  pluginId: string
 }
 }
 
 
 export class Provider extends RequestClient {
 export class Provider extends RequestClient {
-  constructor (uppy: any, opts: ProviderOptions);
-  checkAuth (): Promise<boolean>;
-  authUrl (): string;
-  fileUrl (id: string): string;
-  list (directory: string): Promise<any>;
-  logout (redirect?: string): Promise<any>;
-  static initPlugin(plugin: any, opts: object, defaultOpts?: object): void;
+  constructor (uppy: any, opts: ProviderOptions)
+  checkAuth (): Promise<boolean>
+  authUrl (): string
+  fileUrl (id: string): string
+  list (directory: string): Promise<any>
+  logout (redirect?: string): Promise<any>
+  static initPlugin (plugin: any, opts: object, defaultOpts?: object): void
 }
 }
 
 
 export interface SocketOptions {
 export interface SocketOptions {
-  target: string;
-  autoOpen?: boolean;
+  target: string
+  autoOpen?: boolean
 }
 }
 
 
 export class Socket {
 export class Socket {
-  isOpen: boolean;
-
-  constructor (opts: SocketOptions);
-  open (): void;
-  close (): void;
-  send (action: string, payload: any): void;
-  on (action: string, handler: (param: any) => void): void;
-  once (action: string, handler: (param: any) => void): void;
-  emit (action: string, payload: (param: any) => void): void;
+  isOpen: boolean
+
+  constructor (opts: SocketOptions)
+  open (): void
+  close (): void
+  send (action: string, payload: any): void
+  on (action: string, handler: (param: any) => void): void
+  once (action: string, handler: (param: any) => void): void
+  emit (action: string, payload: (param: any) => void): void
 }
 }

+ 2 - 0
packages/@uppy/companion-client/types/index.test-d.ts

@@ -0,0 +1,2 @@
+import { RequestClient, Provider, Socket } from '../'
+// TODO tests

+ 0 - 71
packages/@uppy/core/types/core-tests.ts

@@ -1,71 +0,0 @@
-import Uppy = require('../')
-import DefaultStore = require('@uppy/store-default')
-
-{
-  const uppy = Uppy()
-  uppy.addFile({
-    data: new Blob([new ArrayBuffer(1024)], { type: 'application/octet-stream' })
-  })
-
-  uppy.upload().then((result) => {
-    result.successful[0] // $ExpectType UppyFile
-    result.failed[0] // $ExpectType UppyFile
-  })
-}
-
-{
-  const store = DefaultStore()
-  const uppy = Uppy({ store })
-}
-
-{
-  const uppy = Uppy()
-  // this doesn't exist but type checking works anyway :)
-  const f = uppy.getFile('virtual')
-  if (f && f.progress && f.progress.uploadStarted === null) {
-    f.progress.uploadStarted = Date.now()
-  }
-
-  if (f && f.response && f.response.status === 200) {
-    f.response.body // $ExpectType any
-  }
-  // f.response!.status === '200' // $ExpectError
-}
-
-{
-  type Meta = {};
-  type ResponseBody = {
-    averageColor: string
-  }
-  const uppy = Uppy()
-  const f = uppy.getFile<Meta, ResponseBody>('virtual')!
-  f.response!.body // $ExpectType ResponseBody
-}
-
-{
-  const uppy = Uppy()
-  uppy.addFile({
-    name: 'empty.json',
-    data: new Blob(['null'], { type: 'application/json' }),
-    meta: { path: 'path/to/file' }
-  })
-}
-
-{
-  const uppy = Uppy()
-  // can emit events with internal event types
-  uppy.emit('upload')
-  uppy.emit('complete', () => {})
-  uppy.emit('error', () => {})
-
-  // can emit events with custom event types
-  uppy.emit('dashboard:modal-closed', () => {})
-
-  // can register listners for internal events
-  uppy.on('upload', () => {})
-  uppy.on('complete', () => {})
-  uppy.on('error', () => {})
-
-  // can register listners on custom events
-  uppy.on('dashboard:modal-closed', () => {})
-}

+ 203 - 113
packages/@uppy/core/types/index.d.ts

@@ -1,104 +1,139 @@
-import UppyUtils = require('@uppy/utils');
+import UppyUtils = require('@uppy/utils')
 
 
 declare module Uppy {
 declare module Uppy {
   // Utility types
   // Utility types
-  type OmitKey<T, Key> = Pick<T, Exclude<keyof T, Key>>;
+  type OmitKey<T, Key> = Pick<T, Exclude<keyof T, Key>>
 
 
   // These are defined in @uppy/utils instead of core so it can be used there without creating import cycles
   // These are defined in @uppy/utils instead of core so it can be used there without creating import cycles
-  export type UppyFile<TMeta extends IndexedObject<any> = {}, TBody extends IndexedObject<any> = {}> = UppyUtils.UppyFile<TMeta, TBody>;
-  export type Store = UppyUtils.Store;
-  export type InternalMetadata = UppyUtils.InternalMetadata;
+  export type UppyFile<
+    TMeta extends IndexedObject<any> = {},
+    TBody extends IndexedObject<any> = {}
+  > = UppyUtils.UppyFile<TMeta, TBody>
+  export type Store = UppyUtils.Store
+  export type InternalMetadata = UppyUtils.InternalMetadata
 
 
   interface IndexedObject<T> {
   interface IndexedObject<T> {
-    [key: string]: T;
-    [key: number]: T;
+    [key: string]: T
+    [key: number]: T
   }
   }
 
 
   interface UploadedUppyFile<TMeta, TBody> extends UppyFile<TMeta, TBody> {
   interface UploadedUppyFile<TMeta, TBody> extends UppyFile<TMeta, TBody> {
-    uploadURL: string;
+    uploadURL: string
   }
   }
 
 
   interface FailedUppyFile<TMeta, TBody> extends UppyFile<TMeta, TBody> {
   interface FailedUppyFile<TMeta, TBody> extends UppyFile<TMeta, TBody> {
-    error: string;
+    error: string
   }
   }
 
 
   // Replace the `meta` property type with one that allows omitting internal metadata; addFile() will add that
   // Replace the `meta` property type with one that allows omitting internal metadata; addFile() will add that
-  type UppyFileWithoutMeta<TMeta, TBody> = OmitKey<UppyFile<TMeta, TBody>, 'meta'>;
-  interface AddFileOptions<TMeta = IndexedObject<any>, TBody = IndexedObject<any>> extends Partial<UppyFileWithoutMeta<TMeta, TBody>> {
+  type UppyFileWithoutMeta<TMeta, TBody> = OmitKey<
+    UppyFile<TMeta, TBody>,
+    'meta'
+  >
+  interface AddFileOptions<
+    TMeta = IndexedObject<any>,
+    TBody = IndexedObject<any>
+  > extends Partial<UppyFileWithoutMeta<TMeta, TBody>> {
     // `.data` is the only required property here.
     // `.data` is the only required property here.
-    data: Blob | File;
-    meta?: Partial<InternalMetadata> & TMeta;
+    data: Blob | File
+    meta?: Partial<InternalMetadata> & TMeta
   }
   }
 
 
   interface PluginOptions {
   interface PluginOptions {
-    id?: string;
+    id?: string
+  }
+  interface DefaultPluginOptions extends PluginOptions {
+    [prop: string]: any
   }
   }
 
 
-  class Plugin {
-    id: string;
-    uppy: Uppy;
-    type: string;
-    constructor(uppy: Uppy, opts?: PluginOptions);
-    getPluginState(): object;
-    setPluginState(update: any): object;
-    update(state?: object): void;
-    mount(target: any, plugin: any): void;
-    render(state: object): void;
-    addTarget(plugin: any): void;
-    unmount(): void;
-    install(): void;
-    uninstall(): void;
+  type PluginTarget = string | Element | typeof Plugin
+
+  class Plugin<TOptions extends PluginOptions = DefaultPluginOptions> {
+    id: string
+    uppy: Uppy
+    type: string
+    constructor(uppy: Uppy, opts?: TOptions)
+    getPluginState(): object
+    setPluginState(update: IndexedObject<any>): object
+    update(state?: object): void
+    mount(target: PluginTarget, plugin: typeof Plugin): void
+    render(state: object): void
+    addTarget<TPlugin extends Plugin>(plugin: TPlugin): void
+    unmount(): void
+    install(): void
+    uninstall(): void
   }
   }
 
 
-  interface LocaleStrings {
-    [key: string]: string | LocaleStrings;
+  type LocaleStrings<TNames extends string> = {
+    [K in TNames]?: string | { [n: number]: string }
   }
   }
-  interface Locale {
-    strings: LocaleStrings;
-    pluralize?: (n: number) => number;
+  interface Locale<TNames extends string = string> {
+    strings: LocaleStrings<TNames>
+    pluralize?: (n: number) => number
   }
   }
 
 
   interface Restrictions {
   interface Restrictions {
-    maxFileSize: number | null;
-    maxNumberOfFiles: number | null;
-    minNumberOfFiles: number | null;
-    allowedFileTypes: string[] | null;
+    maxFileSize?: number | null
+    maxNumberOfFiles?: number | null
+    minNumberOfFiles?: number | null
+    allowedFileTypes?: string[] | null
   }
   }
 
 
-  interface UppyOptions {
-    id: string;
-    autoProceed: boolean;
-    allowMultipleUploads: boolean;
-    debug: boolean;
-    restrictions: Partial<Restrictions>;
-    target: string | Plugin;
-    meta: any;
-    onBeforeFileAdded: (currentFile: UppyFile, files: {[key: string]: UppyFile}) => UppyFile | boolean | undefined;
-    onBeforeUpload: (files: {[key: string]: UppyFile}) => {[key: string]: UppyFile} | boolean;
-    locale: Locale;
-    store: Store;
+  interface UppyOptions<TMeta extends IndexedObject<any> = {}> {
+    id?: string
+    autoProceed?: boolean
+    allowMultipleUploads?: boolean
+    debug?: boolean
+    restrictions?: Restrictions
+    meta?: TMeta
+    onBeforeFileAdded?: (
+      currentFile: UppyFile<TMeta>,
+      files: { [key: string]: UppyFile<TMeta> }
+    ) => UppyFile<TMeta> | boolean | undefined
+    onBeforeUpload?: (files: {
+      [key: string]: UppyFile<TMeta>
+    }) => { [key: string]: UppyFile<TMeta> } | boolean
+    locale?: Locale
+    store?: Store
   }
   }
 
 
-  interface UploadResult<TMeta extends IndexedObject<any> = {}, TBody extends IndexedObject<any> = {}> {
-    successful: UploadedUppyFile<TMeta, TBody>[];
-    failed: FailedUppyFile<TMeta, TBody>[];
+  interface UploadResult<
+    TMeta extends IndexedObject<any> = {},
+    TBody extends IndexedObject<any> = {}
+  > {
+    successful: UploadedUppyFile<TMeta, TBody>[]
+    failed: FailedUppyFile<TMeta, TBody>[]
   }
   }
 
 
-  interface State<TMeta extends IndexedObject<any> = {}, TBody extends IndexedObject<any> = {}> extends IndexedObject<any> {
-    capabilities?: {resumableUploads?: boolean};
-    currentUploads: {};
-    error?: string;
-    files: {[key: string]: UploadedUppyFile<TMeta, TBody> | FailedUppyFile<TMeta, TBody>};
+  interface State<
+    TMeta extends IndexedObject<any> = {},
+    TBody extends IndexedObject<any> = {}
+  > extends IndexedObject<any> {
+    capabilities?: { resumableUploads?: boolean }
+    currentUploads: {}
+    error?: string
+    files: {
+      [key: string]:
+        | UploadedUppyFile<TMeta, TBody>
+        | FailedUppyFile<TMeta, TBody>
+    }
     info?: {
     info?: {
-      isHidden: boolean;
-      type: string;
-      message: string;
-      details: string;
-    };
-    plugins?: IndexedObject<any>;
-    totalProgress: number;
+      isHidden: boolean
+      type: string
+      message: string
+      details: string
+    }
+    plugins?: IndexedObject<any>
+    totalProgress: number
   }
   }
-  type LogLevel = 'info' | 'warning' | 'error';
+
+  type LogLevel = 'info' | 'warning' | 'error'
+
+  /** Enable the old, untyped `uppy.use()` signature. */
+  type LooseTypes = 'loose'
+  /** Disable the old, untyped `uppy.use()` signature. */
+  type StrictTypes = 'strict'
+  type TypeChecking = LooseTypes | StrictTypes
 
 
   // This hack accepts _any_ string for `Event`, but also tricks VSCode and friends into providing autocompletions
   // This hack accepts _any_ string for `Event`, but also tricks VSCode and friends into providing autocompletions
   // for the names listed. https://github.com/microsoft/TypeScript/issues/29729#issuecomment-505826972
   // for the names listed. https://github.com/microsoft/TypeScript/issues/29729#issuecomment-505826972
@@ -106,57 +141,112 @@ declare module Uppy {
   type Event = LiteralUnion<'file-added' | 'file-removed' | 'upload' | 'upload-progress' | 'upload-success' | 'complete' | 'error' | 'upload-error' |
   type Event = LiteralUnion<'file-added' | 'file-removed' | 'upload' | 'upload-progress' | 'upload-success' | 'complete' | 'error' | 'upload-error' |
                'upload-retry' | 'info-visible' | 'info-hidden' | 'cancel-all' | 'restriction-failed' | 'reset-progress'>;
                'upload-retry' | 'info-visible' | 'info-hidden' | 'cancel-all' | 'restriction-failed' | 'reset-progress'>;
 
 
-  class Uppy {
-    constructor(opts?: Partial<UppyOptions>);
-    on<TMeta extends IndexedObject<any> = {}>(event: 'upload-success', callback: (file: UppyFile<TMeta>, body: any, uploadURL: string) => void): Uppy;
-    on<TMeta extends IndexedObject<any> = {}>(event: 'complete', callback: (result: UploadResult<TMeta>) => void): Uppy;
-    on(event: Event, callback: (...args: any[]) => void): Uppy;
-    off(event: Event, callback: any): Uppy;
+  type UploadHandler = (fileIDs: string[]) => Promise<void>
+
+  class Uppy<TUseStrictTypes extends TypeChecking = TypeChecking> {
+    constructor(opts?: UppyOptions)
+    on<TMeta extends IndexedObject<any> = {}>(
+      event: 'upload-success',
+      callback: (file: UppyFile<TMeta>, body: any, uploadURL: string) => void
+    ): this
+    on<TMeta extends IndexedObject<any> = {}>(
+      event: 'complete',
+      callback: (result: UploadResult<TMeta>) => void
+    ): this
+    on(event: Event, callback: (...args: any[]) => void): this
+    off(event: Event, callback: (...args: any[]) => void): this
     /**
     /**
      * For use by plugins only!
      * For use by plugins only!
      */
      */
-    emit(event: Event, ...args: any[]): void;
-    updateAll(state: object): void;
-    setState(patch: object): void;
-    getState<TMeta extends IndexedObject<any> = {}>(): State<TMeta>;
-    readonly state: State;
-    setFileState(fileID: string, state: object): void;
-    resetProgress(): void;
-    addPreProcessor(fn: any): void;
-    removePreProcessor(fn: any): void;
-    addPostProcessor(fn: any): void;
-    removePostProcessor(fn: any): void;
-    addUploader(fn: any): void;
-    removeUploader(fn: any): void;
-    setMeta<TMeta extends IndexedObject<any> = {}>(data: TMeta): void;
-    setFileMeta<TMeta extends IndexedObject<any> = {}>(fileID: string, data: TMeta): void;
-    getFile<TMeta extends IndexedObject<any> = {}, TBody extends IndexedObject<any> = {}>(fileID: string): UppyFile<TMeta, TBody>;
-    getFiles<TMeta extends IndexedObject<any> = {}, TBody extends IndexedObject<any> = {}>(): Array<UppyFile<TMeta, TBody>>;
-    addFile<TMeta extends IndexedObject<any> = {}>(file: AddFileOptions<TMeta>): void;
-    removeFile(fileID: string): void;
-    pauseResume(fileID: string): boolean;
-    pauseAll(): void;
-    resumeAll(): void;
-    retryAll(): void;
-    cancelAll(): void;
-    retryUpload(fileID: string): any;
-    reset(): void;
-    getID(): string;
-    use<T extends typeof Plugin>(pluginClass: T, opts: object): Uppy;
-    getPlugin(name: string): Plugin;
-    iteratePlugins(callback: (plugin: Plugin) => void): void;
-    removePlugin(instance: Plugin): void;
-    close(): void;
-    info(message: string | {message: string; details: string}, type?: LogLevel, duration?: number): void;
-    hideInfo(): void;
-    log(msg: string, type?: LogLevel): void;
-    run(): Uppy;
-    restore<TMeta extends IndexedObject<any> = {}>(uploadID: string): Promise<UploadResult>;
-    addResultData(uploadID: string, data: object): void;
-    upload<TMeta extends IndexedObject<any> = {}>(): Promise<UploadResult>;
+    emit(event: Event, ...args: any[]): void
+    updateAll(state: object): void
+    setState(patch: object): void
+    getState<TMeta extends IndexedObject<any> = {}>(): State<TMeta>
+    readonly state: State
+    setFileState(fileID: string, state: object): void
+    resetProgress(): void
+    addPreProcessor(fn: UploadHandler): void
+    removePreProcessor(fn: UploadHandler): void
+    addPostProcessor(fn: UploadHandler): void
+    removePostProcessor(fn: UploadHandler): void
+    addUploader(fn: UploadHandler): void
+    removeUploader(fn: UploadHandler): void
+    setMeta<TMeta extends IndexedObject<any> = {}>(data: TMeta): void
+    setFileMeta<TMeta extends IndexedObject<any> = {}>(
+      fileID: string,
+      data: TMeta
+    ): void
+    getFile<
+      TMeta extends IndexedObject<any> = {},
+      TBody extends IndexedObject<any> = {}
+    >(fileID: string): UppyFile<TMeta, TBody>
+    getFiles<
+      TMeta extends IndexedObject<any> = {},
+      TBody extends IndexedObject<any> = {}
+    >(): Array<UppyFile<TMeta, TBody>>
+    addFile<TMeta extends IndexedObject<any> = {}>(
+      file: AddFileOptions<TMeta>
+    ): void
+    removeFile(fileID: string): void
+    pauseResume(fileID: string): boolean
+    pauseAll(): void
+    resumeAll(): void
+    retryAll<TMeta extends IndexedObject<any> = {}>(): Promise<UploadResult<TMeta>>
+    cancelAll(): void
+    retryUpload<TMeta extends IndexedObject<any> = {}>(fileID: string): Promise<UploadResult<TMeta>>
+    reset(): void
+    getID(): string
+    /**
+     * Add a plugin to this Uppy instance.
+     */
+    use<TOptions, TInstance extends Plugin<TOptions>>(
+      pluginClass: new (uppy: this, opts: TOptions) => TInstance,
+      opts?: TOptions
+    ): this
+    /**
+     * Fallback `.use()` overload with unchecked plugin options.
+     *
+     * This does not validate that the options you pass in are correct.
+     * We recommend disabling this overload by using the `Uppy<Uppy.StrictTypes>` type, instead of the plain `Uppy` type, to enforce strict typechecking.
+     * This overload will be removed in Uppy 2.0.
+     */
+    use(pluginClass: TUseStrictTypes extends StrictTypes ? never : new (uppy: this, opts: any) => Plugin<any>, opts?: object): this
+    getPlugin(name: string): Plugin
+    iteratePlugins(callback: (plugin: Plugin) => void): void
+    removePlugin(instance: Plugin): void
+    close(): void
+    info(
+      message: string | { message: string; details: string },
+      type?: LogLevel,
+      duration?: number
+    ): void
+    hideInfo(): void
+    log(msg: string, type?: LogLevel): void
+    /**
+     * Obsolete: do not use. This method does nothing and will be removed in a future release.
+     */
+    run(): this
+    restore<TMeta extends IndexedObject<any> = {}>(
+      uploadID: string
+    ): Promise<UploadResult<TMeta>>
+    addResultData(uploadID: string, data: object): void
+    upload<TMeta extends IndexedObject<any> = {}>(): Promise<UploadResult<TMeta>>
   }
   }
 }
 }
 
 
-declare function Uppy(opts?: Partial<Uppy.UppyOptions>): Uppy.Uppy;
-
-export = Uppy;
+/**
+ * Create an uppy instance.
+ *
+ * By default, Uppy's `.use(Plugin, options)` method uses loose type checking.
+ * In Uppy 2.0, the `.use()` method will get a stricter type signature. You can enable strict type checking of plugin classes and their options today by using:
+ * ```ts
+ * const uppy = Uppy<Uppy.StrictTypes>()
+ * ```
+ * Make sure to also declare any variables and class properties with the `StrictTypes` parameter:
+ * ```ts
+ * private uppy: Uppy<Uppy.StrictTypes>;
+ * ```
+ */
+declare function Uppy<TUseStrictTypes extends Uppy.TypeChecking = Uppy.TypeChecking>(opts?: Uppy.UppyOptions): Uppy.Uppy<TUseStrictTypes>
+
+export = Uppy

+ 91 - 0
packages/@uppy/core/types/index.test-d.ts

@@ -0,0 +1,91 @@
+import { expectError, expectType } from 'tsd'
+import Uppy = require('../')
+import DefaultStore = require('@uppy/store-default')
+
+{
+  const uppy = Uppy<Uppy.StrictTypes>()
+  uppy.addFile({
+    data: new Blob([new ArrayBuffer(1024)], {
+      type: 'application/octet-stream'
+    })
+  })
+
+  uppy.upload().then(result => {
+    expectType<Uppy.UploadedUppyFile<{}, {}>>(result.successful[0])
+    expectType<Uppy.FailedUppyFile<{}, {}>>(result.failed[0])
+  })
+}
+
+{
+  const store = DefaultStore()
+  const uppy = Uppy<Uppy.StrictTypes>({ store })
+}
+
+{
+  const uppy = Uppy<Uppy.StrictTypes>()
+  // this doesn't exist but type checking works anyway :)
+  const f = uppy.getFile('virtual')
+  if (f && f.progress && f.progress.uploadStarted === null) {
+    f.progress.uploadStarted = Date.now()
+  }
+
+  if (f && f.response && f.response.status === 200) {
+    expectType(f.response.body)
+  }
+  expectType<number>(f.response!.status)
+}
+
+{
+  type Meta = {}
+  type ResponseBody = {
+    averageColor: string
+  }
+  const uppy = Uppy<Uppy.StrictTypes>()
+  const f = uppy.getFile<Meta, ResponseBody>('virtual')!
+  expectType<ResponseBody>(f.response!.body)
+}
+
+{
+  const uppy = Uppy<Uppy.StrictTypes>()
+  uppy.addFile({
+    name: 'empty.json',
+    data: new Blob(['null'], { type: 'application/json' }),
+    meta: { path: 'path/to/file' }
+  })
+}
+
+{
+  interface SomeOptions extends Uppy.PluginOptions {
+    types: 'are checked'
+  }
+  class SomePlugin extends Uppy.Plugin<SomeOptions> {}
+  const untypedUppy = Uppy()
+  untypedUppy.use(SomePlugin, { types: 'are unchecked' })
+  const typedUppy = Uppy<Uppy.StrictTypes>()
+  expectError(typedUppy.use(SomePlugin, { types: 'are unchecked' }))
+  typedUppy.use(SomePlugin, { types: 'are checked' })
+
+  // strictly-typed instance can be cast to a loosely-typed instance
+  const widenUppy: Uppy.Uppy = Uppy<Uppy.StrictTypes>()
+  // and disables the type checking
+  widenUppy.use(SomePlugin, { random: 'nonsense' })
+}
+
+{
+  const uppy = Uppy()
+  // can emit events with internal event types
+  uppy.emit('upload')
+  uppy.emit('complete', () => {})
+  uppy.emit('error', () => {})
+
+  // can emit events with custom event types
+  uppy.emit('dashboard:modal-closed', () => {})
+
+  // can register listners for internal events
+  uppy.on('upload', () => {})
+  uppy.on('complete', () => {})
+  uppy.on('error', () => {})
+
+  // can register listners on custom events
+  uppy.on('dashboard:modal-closed', () => {})
+}

+ 0 - 32
packages/@uppy/dashboard/types/dashboard-tests.ts

@@ -1,32 +0,0 @@
-import Uppy = require('@uppy/core')
-import Dashboard = require('../')
-
-{
-  const uppy = Uppy()
-  uppy.use(Dashboard, {
-    target: 'body'
-  })
-
-  const plugin = uppy.getPlugin('Dashboard') as Dashboard
-  plugin.openModal()
-  plugin.isModalOpen() // $ExpectType boolean
-  plugin.closeModal()
-}
-
-{
-  const uppy = Uppy()
-  uppy.use(Dashboard, <Partial<Dashboard.DashboardOptions>>{
-    width: '100%',
-    height: 700,
-    metaFields: [
-      { id: 'caption', name: 'Caption' },
-      { id: 'license', name: 'License', placeholder: 'Creative Commons, Apache 2.0, ...' },
-    ]
-  })
-}
-
-{
-  const uppy = Uppy()
-  // $ExpectError
-  uppy.use(Dashboard, { height: {} })
-}

+ 45 - 102
packages/@uppy/dashboard/types/index.d.ts

@@ -1,113 +1,56 @@
-import Uppy = require('@uppy/core');
+import Uppy = require('@uppy/core')
+import StatusBar = require('@uppy/status-bar')
+import DashboardLocale = require('./generatedLocale')
 
 
 interface MetaField {
 interface MetaField {
-  id: string;
-  name: string;
-  placeholder?: string;
+  id: string
+  name: string
+  placeholder?: string
 }
 }
 
 
 declare module Dashboard {
 declare module Dashboard {
-  interface DashboardLocale {
-    strings: {
-      closeModal: string,
-      importFrom: string,
-      addingMoreFiles: string,
-      addMoreFiles: string,
-      dashboardWindowTitle: string,
-      dashboardTitle: string,
-      copyLinkToClipboardSuccess: string,
-      copyLinkToClipboardFallback: string,
-      copyLink: string,
-      link: string,
-      fileSource: string,
-      done: string,
-      back: string,
-      addMore: string,
-      removeFile: string,
-      editFile: string,
-      editing: string,
-      edit: string,
-      finishEditingFile: string,
-      saveChanges: string,
-      cancel: string,
-      myDevice: string,
-      dropPasteImport: string,
-      dropPaste: string,
-      dropHint: string,
-      browse: string,
-      uploadComplete: string,
-      uploadPaused: string,
-      resumeUpload: string,
-      pauseUpload: string,
-      retryUpload: string,
-      cancelUpload: string,
-      xFilesSelected: {
-        0: string,
-        1: string,
-        2: string
-      },
-      uploadingXFiles: {
-        0: string,
-        1: string,
-        2: string
-      },
-      processingXFiles: {
-        0: string,
-        1: string,
-        2: string
-      },
-      poweredBy: string
-    }
-  }
-
   interface DashboardOptions extends Uppy.PluginOptions {
   interface DashboardOptions extends Uppy.PluginOptions {
-    animateOpenClose: boolean;
-    browserBackButtonClose: boolean
-    closeAfterFinish: boolean;
-    closeModalOnClickOutside: boolean;
-    disableInformer: boolean;
-    disablePageScrollWhenModalOpen: boolean;
-    disableStatusBar: boolean;
-    disableThumbnailGenerator: boolean;
-    height: string | number;
-    hideCancelButton: boolean;
-    hidePauseResumeButton: boolean;
-    hideProgressAfterFinish: boolean;
-    hideRetryButton: boolean;
-    hideUploadButton: boolean;
-    inline: boolean;
-    locale: DashboardLocale;
-    metaFields: MetaField[];
-    note: string | null;
-    onRequestCloseModal: () => void;
-    plugins: string[];
-    proudlyDisplayPoweredByUppy: boolean;
-    showLinkToFileUploadResult: boolean;
-    showProgressDetails: boolean;
-    showSelectedFiles: boolean;
-    target: string;
-    thumbnailWidth: number;
-    trigger: string;
-    width: string | number;
+    animateOpenClose?: boolean
+    browserBackButtonClose?: boolean
+    closeAfterFinish?: boolean
+    closeModalOnClickOutside?: boolean
+    disableInformer?: boolean
+    disablePageScrollWhenModalOpen?: boolean
+    disableStatusBar?: boolean
+    disableThumbnailGenerator?: boolean
+    height?: string | number
+    hideCancelButton?: boolean
+    hidePauseResumeButton?: boolean
+    hideProgressAfterFinish?: boolean
+    hideRetryButton?: boolean
+    hideUploadButton?: boolean
+    inline?: boolean
+    locale?: DashboardLocale & StatusBar.StatusBarLocale
+    metaFields?: MetaField[]
+    note?: string | null
+    onRequestCloseModal?: () => void
+    plugins?: string[]
+    proudlyDisplayPoweredByUppy?: boolean
+    showLinkToFileUploadResult?: boolean
+    showProgressDetails?: boolean
+    showSelectedFiles?: boolean
+    replaceTargetContent?: boolean
+    target?: Uppy.PluginTarget
+    thumbnailWidth?: number
+    trigger?: string
+    width?: string | number
   }
   }
 }
 }
 
 
-declare class Dashboard extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<Dashboard.DashboardOptions>);
-  addTarget(plugin: Uppy.Plugin): HTMLElement;
-  hideAllPanels(): void;
-  openModal(): void;
-  closeModal(): void;
-  isModalOpen(): boolean;
-  render(state: object): void;
-  install(): void;
-  uninstall(): void;
+declare class Dashboard extends Uppy.Plugin<Dashboard.DashboardOptions> {
+  addTarget (plugin: Uppy.Plugin): HTMLElement
+  hideAllPanels (): void
+  openModal (): void
+  closeModal (): void
+  isModalOpen (): boolean
+  render (state: object): void
+  install (): void
+  uninstall (): void
 }
 }
 
 
-export = Dashboard;
-
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof Dashboard, opts: Partial<Dashboard.DashboardOptions>): Uppy.Uppy;
-  }
-}
+export = Dashboard

+ 64 - 0
packages/@uppy/dashboard/types/index.test-d.ts

@@ -0,0 +1,64 @@
+import { expectType, expectError } from 'tsd'
+import Uppy = require('@uppy/core')
+import Dashboard = require('../')
+
+{
+  const uppy = Uppy<Uppy.StrictTypes>()
+  uppy.use(Dashboard, {
+    target: 'body'
+  })
+
+  const plugin = uppy.getPlugin('Dashboard') as Dashboard
+  plugin.openModal()
+  expectType<boolean>(plugin.isModalOpen())
+  plugin.closeModal()
+}
+
+{
+  const uppy = Uppy<Uppy.StrictTypes>()
+  uppy.use(Dashboard, {
+    width: '100%',
+    height: 700,
+    metaFields: [
+      { id: 'caption', name: 'Caption' },
+      {
+        id: 'license',
+        name: 'License',
+        placeholder: 'Creative Commons, Apache 2.0, ...'
+      }
+    ]
+  })
+}
+
+{
+  const uppy = Uppy<Uppy.StrictTypes>()
+  uppy.use(Dashboard, {
+    locale: {
+      strings: {
+        // Dashboard string
+        addMoreFiles: 'yaddayadda',
+        // StatusBar string
+        uploading: '^^^^'
+      }
+    }
+  })
+  expectError(uppy.use(Dashboard, {
+    locale: {
+      strings: {
+        somethingThatDoesNotExist: 'wrong'
+      }
+    }
+  }))
+  const wrongType = 1234
+  expectError(uppy.use(Dashboard, {
+    locale: {
+      strings: {
+        addMoreFiles: wrongType
+      }
+    }
+  }))
+}
+{
+  const uppy = Uppy<Uppy.StrictTypes>()
+  expectError(uppy.use(Dashboard, { height: {} }))
+}

+ 12 - 16
packages/@uppy/drag-drop/types/index.d.ts

@@ -1,23 +1,19 @@
-import Uppy = require('@uppy/core');
+import Uppy = require('@uppy/core')
+import DragDropLocale = require('./generatedLocale')
 
 
 declare module DragDrop {
 declare module DragDrop {
   interface DragDropOptions extends Uppy.PluginOptions {
   interface DragDropOptions extends Uppy.PluginOptions {
-    inputName: string;
-    allowMultipleFiles: boolean;
-    width: string;
-    height: string;
-    note: string;
+    replaceTargetContent?: boolean
+    target?: Uppy.PluginTarget
+    inputName?: string
+    allowMultipleFiles?: boolean
+    width?: string
+    height?: string
+    note?: string
+    locale?: DragDropLocale
   }
   }
 }
 }
 
 
-declare class DragDrop extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<DragDrop.DragDropOptions>);
-}
-
-export = DragDrop;
+declare class DragDrop extends Uppy.Plugin<DragDrop.DragDropOptions> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof DragDrop, opts: Partial<DragDrop.DragDropOptions>): Uppy.Uppy;
-  }
-}
+export = DragDrop

+ 2 - 0
packages/@uppy/drag-drop/types/index.test-d.ts

@@ -0,0 +1,2 @@
+import DragDrop = require('../')
+// TODO implement

+ 11 - 15
packages/@uppy/dropbox/types/index.d.ts

@@ -1,21 +1,17 @@
-import Uppy = require('@uppy/core');
-import CompanionClient = require('@uppy/companion-client');
+import Uppy = require('@uppy/core')
+import CompanionClient = require('@uppy/companion-client')
 
 
 declare module Dropbox {
 declare module Dropbox {
-  interface DropboxOptions extends Uppy.PluginOptions, CompanionClient.ProviderOptions {
-    companionUrl: string;
-    companionAllowedHosts: string | RegExp | Array<string | RegExp>;
+  interface DropboxOptions
+    extends Uppy.PluginOptions,
+      CompanionClient.PublicProviderOptions {
+    replaceTargetContent?: boolean
+    target?: Uppy.PluginTarget
+    title?: string
+    storage?: CompanionClient.TokenStorage
   }
   }
 }
 }
 
 
-declare class Dropbox extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<Dropbox.DropboxOptions>);
-}
-
-export = Dropbox;
+declare class Dropbox extends Uppy.Plugin<Dropbox.DropboxOptions> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof Dropbox, opts: Partial<Dropbox.DropboxOptions>): Uppy.Uppy;
-  }
-}
+export = Dropbox

+ 2 - 0
packages/@uppy/dropbox/types/index.test-d.ts

@@ -0,0 +1,2 @@
+import Dropbox = require('../')
+// TODO implement

+ 11 - 15
packages/@uppy/facebook/types/index.d.ts

@@ -1,21 +1,17 @@
-import Uppy = require('@uppy/core');
-import CompanionClient = require('@uppy/companion-client');
+import Uppy = require('@uppy/core')
+import CompanionClient = require('@uppy/companion-client')
 
 
 declare module Facebook {
 declare module Facebook {
-  interface FacebookOptions extends Uppy.PluginOptions, CompanionClient.ProviderOptions {
-    companionUrl: string;
-    companionAllowedHosts: string | RegExp | Array<string | RegExp>;
+  interface FacebookOptions
+    extends Uppy.PluginOptions,
+      CompanionClient.PublicProviderOptions {
+    replaceTargetContent?: boolean
+    target?: Uppy.PluginTarget
+    title?: string
+    storage?: CompanionClient.TokenStorage
   }
   }
 }
 }
 
 
-declare class Facebook extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<Facebook.FacebookOptions>);
-}
-
-export = Facebook;
+declare class Facebook extends Uppy.Plugin<Facebook.FacebookOptions> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof Facebook, opts: Partial<Facebook.FacebookOptions>): Uppy.Uppy;
-  }
-}
+export = Facebook

+ 2 - 0
packages/@uppy/facebook/types/index.test-d.ts

@@ -0,0 +1,2 @@
+import Facebook = require('../')
+// TODO implement

+ 10 - 14
packages/@uppy/file-input/types/index.d.ts

@@ -1,20 +1,16 @@
-import Uppy = require('@uppy/core');
+import Uppy = require('@uppy/core')
+import FileInputLocale = require('./generatedLocale')
 
 
 declare module FileInput {
 declare module FileInput {
-  interface FileInputOptions extends Uppy.PluginOptions {
-    pretty: boolean;
-    inputName: string;
+  export interface FileInputOptions extends Uppy.PluginOptions {
+    replaceTargetContent?: boolean
+    target?: Uppy.PluginTarget
+    pretty?: boolean
+    inputName?: string
+    locale?: FileInputLocale
   }
   }
 }
 }
 
 
-declare class FileInput extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<FileInput.FileInputOptions>);
-}
-
-export = FileInput;
+declare class FileInput extends Uppy.Plugin<FileInput.FileInputOptions> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof FileInput, opts: Partial<FileInput.FileInputOptions>): Uppy.Uppy;
-  }
-}
+export = FileInput

+ 1 - 0
packages/@uppy/file-input/types/index.test-d.ts

@@ -0,0 +1 @@
+import FileInput = require('../')

+ 1 - 1
packages/@uppy/form/src/index.js

@@ -29,7 +29,7 @@ module.exports = class Form extends Plugin {
     }
     }
 
 
     // merge default options with the ones set by user
     // merge default options with the ones set by user
-    this.opts = Object.assign({}, defaultOptions, opts)
+    this.opts = { ...defaultOptions, ...opts }
 
 
     this.handleFormSubmit = this.handleFormSubmit.bind(this)
     this.handleFormSubmit = this.handleFormSubmit.bind(this)
     this.handleUploadStart = this.handleUploadStart.bind(this)
     this.handleUploadStart = this.handleUploadStart.bind(this)

+ 11 - 16
packages/@uppy/form/types/index.d.ts

@@ -1,23 +1,18 @@
-import Uppy = require('@uppy/core');
+import Uppy = require('@uppy/core')
 
 
 declare module Form {
 declare module Form {
   interface FormOptions extends Uppy.PluginOptions {
   interface FormOptions extends Uppy.PluginOptions {
-    getMetaFromForm: boolean;
-    addResultToForm: boolean;
-    submitOnSuccess: boolean;
-    triggerUploadOnSubmit: boolean;
-    resultName: string;
+    replaceTargetContent?: boolean
+    target?: Uppy.PluginTarget
+    resultName?: string
+    getMetaFromForm?: boolean
+    addResultToForm?: boolean
+    multipleResults?: boolean
+    submitOnSuccess?: boolean
+    triggerUploadOnSubmit?: boolean
   }
   }
 }
 }
 
 
-declare class Form extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<Form.FormOptions>);
-}
-
-export = Form;
+declare class Form extends Uppy.Plugin<Form.FormOptions> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof Form, opts: Partial<Form.FormOptions>): Uppy.Uppy;
-  }
-}
+export = Form

+ 1 - 0
packages/@uppy/form/types/index.test-d.ts

@@ -0,0 +1 @@
+import Form = require('../')

+ 8 - 14
packages/@uppy/golden-retriever/types/index.d.ts

@@ -1,21 +1,15 @@
-import Uppy = require('@uppy/core');
+import Uppy = require('@uppy/core')
 
 
 declare module GoldenRetriever {
 declare module GoldenRetriever {
   interface GoldenRetrieverOptions extends Uppy.PluginOptions {
   interface GoldenRetrieverOptions extends Uppy.PluginOptions {
-    expires: number;
-    serviceWorker: boolean;
-    indexedDB: any;
+    expires?: number
+    serviceWorker?: boolean
+    indexedDB?: any
   }
   }
 }
 }
 
 
-declare class GoldenRetriever extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<GoldenRetriever.GoldenRetrieverOptions>);
-}
-
-export = GoldenRetriever;
+declare class GoldenRetriever extends Uppy.Plugin<
+  GoldenRetriever.GoldenRetrieverOptions
+> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof GoldenRetriever, opts: Partial<GoldenRetriever.GoldenRetrieverOptions>): Uppy.Uppy;
-  }
-}
+export = GoldenRetriever

+ 1 - 0
packages/@uppy/golden-retriever/types/index.test-d.ts

@@ -0,0 +1 @@
+import GoldenRetriever = require('../')

+ 11 - 15
packages/@uppy/google-drive/types/index.d.ts

@@ -1,21 +1,17 @@
-import Uppy = require('@uppy/core');
-import CompanionClient = require('@uppy/companion-client');
+import Uppy = require('@uppy/core')
+import CompanionClient = require('@uppy/companion-client')
 
 
 declare module GoogleDrive {
 declare module GoogleDrive {
-  interface GoogleDriveOptions extends Uppy.PluginOptions, CompanionClient.ProviderOptions {
-    companionUrl: string;
-    companionAllowedHosts: string | RegExp | Array<string | RegExp>;
+  interface GoogleDriveOptions
+    extends Uppy.PluginOptions,
+      CompanionClient.PublicProviderOptions {
+    replaceTargetContent?: boolean
+    target?: Uppy.PluginTarget
+    title?: string
+    storage?: CompanionClient.TokenStorage
   }
   }
 }
 }
 
 
-declare class GoogleDrive extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<GoogleDrive.GoogleDriveOptions>);
-}
-
-export = GoogleDrive;
+declare class GoogleDrive extends Uppy.Plugin<GoogleDrive.GoogleDriveOptions> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof GoogleDrive, opts: Partial<GoogleDrive.GoogleDriveOptions>): Uppy.Uppy;
-  }
-}
+export = GoogleDrive

+ 9 - 0
packages/@uppy/google-drive/types/index.test-d.ts

@@ -0,0 +1,9 @@
+import Uppy = require('@uppy/core')
+import GoogleDrive = require('../')
+
+class SomePlugin extends Uppy.Plugin<{}> {}
+
+const uppy = Uppy<Uppy.StrictTypes>()
+uppy.use(GoogleDrive, { companionUrl: '' })
+uppy.use(GoogleDrive, { target: SomePlugin, companionUrl: '' })
+uppy.use(GoogleDrive, { target: document.querySelector('#gdrive')!, companionUrl: '' })

+ 5 - 19
packages/@uppy/informer/types/index.d.ts

@@ -1,26 +1,12 @@
-import Uppy = require('@uppy/core');
+import Uppy = require('@uppy/core')
 
 
 declare module Informer {
 declare module Informer {
-  interface Color {
-    bg: string | number;
-    text: string | number;
-  }
-
   interface InformerOptions extends Uppy.PluginOptions {
   interface InformerOptions extends Uppy.PluginOptions {
-    typeColors: {
-      [type: string]: Color
-    };
+    replaceTargetContent?: boolean
+    target?: Uppy.PluginTarget
   }
   }
 }
 }
 
 
-declare class Informer extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<Informer.InformerOptions>);
-}
-
-export = Informer;
+declare class Informer extends Uppy.Plugin<Informer.InformerOptions> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof Informer, opts: Partial<Informer.InformerOptions>): Uppy.Uppy;
-  }
-}
+export = Informer

+ 1 - 0
packages/@uppy/informer/types/index.test-d.ts

@@ -0,0 +1 @@
+import Informer = require('../')

+ 11 - 15
packages/@uppy/instagram/types/index.d.ts

@@ -1,21 +1,17 @@
-import Uppy = require('@uppy/core');
-import CompanionClient = require('@uppy/companion-client');
+import Uppy = require('@uppy/core')
+import CompanionClient = require('@uppy/companion-client')
 
 
 declare module Instagram {
 declare module Instagram {
-  interface InstagramOptions extends Uppy.PluginOptions, CompanionClient.ProviderOptions {
-    companionUrl: string;
-    companionAllowedHosts: string | RegExp | Array<string | RegExp>;
+  interface InstagramOptions
+    extends Uppy.PluginOptions,
+      CompanionClient.PublicProviderOptions {
+    replaceTargetContent?: boolean
+    target?: Uppy.PluginTarget
+    title?: string
+    storage?: CompanionClient.TokenStorage
   }
   }
 }
 }
 
 
-declare class Instagram extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<Instagram.InstagramOptions>);
-}
-
-export = Instagram;
+declare class Instagram extends Uppy.Plugin<Instagram.InstagramOptions> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof Instagram, opts: Partial<Instagram.InstagramOptions>): Uppy.Uppy;
-  }
-}
+export = Instagram

+ 2 - 0
packages/@uppy/instagram/types/index.test-d.ts

@@ -0,0 +1,2 @@
+import Instagram = require('../')
+// TODO implement

+ 11 - 15
packages/@uppy/onedrive/types/index.d.ts

@@ -1,21 +1,17 @@
-import Uppy = require('@uppy/core');
-import CompanionClient = require('@uppy/companion-client');
+import Uppy = require('@uppy/core')
+import CompanionClient = require('@uppy/companion-client')
 
 
 declare module OneDrive {
 declare module OneDrive {
-  interface OneDriveOptions extends Uppy.PluginOptions, CompanionClient.ProviderOptions {
-    companionUrl: string;
-    companionAllowedHosts: string | RegExp | Array<string | RegExp>;
+  interface OneDriveOptions
+    extends Uppy.PluginOptions,
+      CompanionClient.PublicProviderOptions {
+    replaceTargetContent?: boolean
+    target?: Uppy.PluginTarget
+    title?: string
+    storage?: CompanionClient.TokenStorage
   }
   }
 }
 }
 
 
-declare class OneDrive extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<OneDrive.OneDriveOptions>);
-}
-
-export = OneDrive;
+declare class OneDrive extends Uppy.Plugin<OneDrive.OneDriveOptions> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof OneDrive, opts: Partial<OneDrive.OneDriveOptions>): Uppy.Uppy;
-  }
-}
+export = OneDrive

+ 2 - 0
packages/@uppy/onedrive/types/index.test-d.ts

@@ -0,0 +1,2 @@
+import OneDrive = require('../')
+// TODO implement

+ 7 - 13
packages/@uppy/progress-bar/types/index.d.ts

@@ -1,20 +1,14 @@
-import Uppy = require('@uppy/core');
+import Uppy = require('@uppy/core')
 
 
 declare module ProgressBar {
 declare module ProgressBar {
   interface ProgressBarOptions extends Uppy.PluginOptions {
   interface ProgressBarOptions extends Uppy.PluginOptions {
-    hideAfterFinish: boolean;
-    fixed: boolean;
+    replaceTargetContent?: boolean
+    target?: Uppy.PluginTarget
+    hideAfterFinish?: boolean
+    fixed?: boolean
   }
   }
 }
 }
 
 
-declare class ProgressBar extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<ProgressBar.ProgressBarOptions>);
-}
-
-export = ProgressBar;
+declare class ProgressBar extends Uppy.Plugin<ProgressBar.ProgressBarOptions> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof ProgressBar, opts: Partial<ProgressBar.ProgressBarOptions>): Uppy.Uppy;
-  }
-}
+export = ProgressBar

+ 2 - 0
packages/@uppy/progress-bar/types/index.test-d.ts

@@ -0,0 +1,2 @@
+import ProgressBar = require('../')
+// TODO implement

+ 11 - 2
packages/@uppy/react/src/CommonTypes.d.ts

@@ -1,4 +1,13 @@
 import UppyCore = require('@uppy/core');
 import UppyCore = require('@uppy/core');
 
 
-export interface Uppy extends UppyCore.Uppy {}
-export interface Locale extends UppyCore.Locale {}
+export type Uppy = UppyCore.Uppy
+export type Locale = UppyCore.Locale
+
+// Helper to exclude a key
+export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
+
+// If I use the helper it doesn't work, I think because 'target' is not a `keyof T` while
+// the generic T is unknown, so will just use the expansion here
+type OmitTarget<T> = Pick<T, Exclude<keyof T, 'target' | 'replaceTargetContent'>>
+type WithBaseUppyProps<T> = T & { uppy: Uppy }
+export type ToUppyProps<T> = WithBaseUppyProps<OmitTarget<T>>

+ 13 - 32
packages/@uppy/react/src/Dashboard.d.ts

@@ -1,39 +1,20 @@
-import { Uppy, Locale } from './CommonTypes';
+import { Omit, ToUppyProps } from './CommonTypes'
+import Dashboard = require('@uppy/dashboard')
 
 
-interface MetaField {
-  id: string;
-  name: string;
-  placeholder?: string;
-}
+// This type is mapped into `DashboardProps` below so IntelliSense doesn't display this big mess of nested types
+type DashboardPropsInner = Omit<
+  ToUppyProps<Dashboard.DashboardOptions>,
+  // Remove the modal-only props
+  'animateOpenClose' | 'browserBackButtonClose' | 'inline' | 'onRequestCloseModal' | 'trigger'
+>
 
 
-export interface DashboardProps {
-  uppy: Uppy;
-  inline?: boolean;
-  plugins?: Array<string>;
-  trigger?: string;
-  width?: number;
-  height?: number;
-  showLinkToFileUploadResult?: boolean;
-  showProgressDetails?: boolean;
-  hideUploadButton?: boolean;
-  hideRetryButton?: boolean;
-  hidePauseResumeButton?: boolean;
-  hideCancelButton?: boolean;
-  hideProgressAfterFinish?: boolean;
-  showSelectedFiles?: boolean;
-  note?: string;
-  metaFields?: Array<MetaField>;
-  proudlyDisplayPoweredByUppy?: boolean;
-  disableStatusBar?: boolean;
-  disableInformer?: boolean;
-  disableThumbnailGenerator?: boolean;
-  thumbnailWidth?: number;
-  locale?: Locale;
+export type DashboardProps = {
+   [K in keyof DashboardPropsInner]: DashboardPropsInner[K]
 }
 }
 
 
 /**
 /**
  * React Component that renders a Dashboard for an Uppy instance. This component
  * React Component that renders a Dashboard for an Uppy instance. This component
- * renders the Dashboard inline; so you can put it anywhere you want.
+ * renders the Dashboard inline so you can put it anywhere you want.
  */
  */
-declare const Dashboard: React.ComponentType<DashboardProps>;
-export default Dashboard;
+declare const DashboardComponent: React.ComponentType<DashboardProps>
+export default DashboardComponent

+ 5 - 10
packages/@uppy/react/src/DashboardModal.d.ts

@@ -1,18 +1,13 @@
-import { DashboardProps } from './Dashboard';
+import { DashboardProps } from './Dashboard'
 
 
 export interface DashboardModalProps extends DashboardProps {
 export interface DashboardModalProps extends DashboardProps {
-  target?: string | HTMLElement;
-  open?: boolean;
-  onRequestClose?: VoidFunction;
-  closeAfterFinish?: boolean;
-  animateOpenClose?: boolean;
-  closeModalOnClickOutside?: boolean;
-  disablePageScrollWhenModalOpen?: boolean;
+  open?: boolean
+  onRequestClose?: VoidFunction
 }
 }
 
 
 /**
 /**
  * React Component that renders a Dashboard for an Uppy instance in a Modal
  * React Component that renders a Dashboard for an Uppy instance in a Modal
  * dialog. Visibility of the Modal is toggled using the `open` prop.
  * dialog. Visibility of the Modal is toggled using the `open` prop.
  */
  */
-declare const DashboardModal: React.ComponentType<DashboardModalProps>;
-export default DashboardModal;
+declare const DashboardModal: React.ComponentType<DashboardModalProps>
+export default DashboardModal

+ 5 - 7
packages/@uppy/react/src/DragDrop.d.ts

@@ -1,13 +1,11 @@
-import { Uppy, Locale } from './CommonTypes';
+import { ToUppyProps } from './CommonTypes'
+import DragDrop = require('@uppy/drag-drop')
 
 
-export interface DragDropProps {
-  uppy: Uppy;
-  locale?: Locale;
-}
+export type DragDropProps = ToUppyProps<DragDrop.DragDropOptions>
 
 
 /**
 /**
  * React component that renders an area in which files can be dropped to be
  * React component that renders an area in which files can be dropped to be
  * uploaded.
  * uploaded.
  */
  */
-declare const DragDrop: React.ComponentType<DragDropProps>;
-export default DragDrop;
+declare const DragDropComponent: React.ComponentType<DragDropProps>;
+export default DragDropComponent;

+ 5 - 8
packages/@uppy/react/src/ProgressBar.d.ts

@@ -1,13 +1,10 @@
-import { Uppy } from './CommonTypes';
+import { ToUppyProps } from './CommonTypes'
+import ProgressBar = require('@uppy/progress-bar')
 
 
-export interface ProgressBarProps {
-  uppy: Uppy;
-  fixed?: boolean;
-  hideAfterFinish?: boolean;
-}
+export type ProgressBarProps = ToUppyProps<ProgressBar.ProgressBarOptions>
 
 
 /**
 /**
  * React component that renders a progress bar at the top of the page.
  * React component that renders a progress bar at the top of the page.
  */
  */
-declare const ProgressBar: React.ComponentType<ProgressBarProps>;
-export default ProgressBar;
+declare const ProgressBarComponent: React.ComponentType<ProgressBarProps>
+export default ProgressBarComponent

+ 5 - 8
packages/@uppy/react/src/StatusBar.d.ts

@@ -1,14 +1,11 @@
-import { Uppy } from './CommonTypes';
+import { ToUppyProps } from './CommonTypes'
+import StatusBar = require('@uppy/status-bar')
 
 
-export interface StatusBarProps {
-  uppy: Uppy;
-  showProgressDetails?: boolean;
-  hideAfterFinish?: boolean;
-}
+export type StatusBarProps = ToUppyProps<StatusBar.StatusBarOptions>
 
 
 /**
 /**
  * React component that renders a status bar containing upload progress and speed,
  * React component that renders a status bar containing upload progress and speed,
  * processing progress and pause/resume/cancel controls.
  * processing progress and pause/resume/cancel controls.
  */
  */
-declare const StatusBar: React.ComponentType<StatusBarProps>;
-export default StatusBar;
+declare const StatusBarComponent: React.ComponentType<StatusBarProps>
+export default StatusBarComponent

+ 6 - 6
packages/@uppy/react/types/index.d.ts

@@ -1,7 +1,7 @@
-import * as React from 'react';
+import * as React from 'react'
 
 
-export { default as Dashboard } from '../src/Dashboard';
-export { default as DashboardModal } from '../src/DashboardModal';
-export { default as DragDrop } from '../src/DragDrop';
-export { default as ProgressBar } from '../src/ProgressBar';
-export { default as StatusBar } from '../src/StatusBar';
+export { default as Dashboard } from '../src/Dashboard'
+export { default as DashboardModal } from '../src/DashboardModal'
+export { default as DragDrop } from '../src/DragDrop'
+export { default as ProgressBar } from '../src/ProgressBar'
+export { default as StatusBar } from '../src/StatusBar'

+ 20 - 0
packages/@uppy/react/types/index.test-d.tsx

@@ -0,0 +1,20 @@
+import React = require('react')
+import Uppy = require('@uppy/core')
+import { expectError } from 'tsd'
+import * as components from '../'
+
+const uppy = Uppy<Uppy.StrictTypes>()
+
+function TestComponent() {
+    return (
+        <components.Dashboard
+            uppy={uppy}
+            closeAfterFinish
+            hideCancelButton
+        />
+    )
+}
+
+// inline option should be removed from proptypes because it is always overridden
+// by the component
+expectError(<components.Dashboard inline />)

+ 6 - 13
packages/@uppy/redux-dev-tools/types/index.d.ts

@@ -1,18 +1,11 @@
-import Uppy = require('@uppy/core');
+import Uppy = require('@uppy/core')
 
 
 declare module ReduxDevTools {
 declare module ReduxDevTools {
-  interface ReduxDevToolsOptions extends Uppy.PluginOptions {
-  }
+  interface ReduxDevToolsOptions extends Uppy.PluginOptions {}
 }
 }
 
 
-declare class ReduxDevTools extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<ReduxDevTools.ReduxDevToolsOptions>);
-}
-
-export = ReduxDevTools;
+declare class ReduxDevTools extends Uppy.Plugin<
+  ReduxDevTools.ReduxDevToolsOptions
+> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof ReduxDevTools, opts: Partial<ReduxDevTools.ReduxDevToolsOptions>): Uppy.Uppy;
-  }
-}
+export = ReduxDevTools

+ 1 - 0
packages/@uppy/redux-dev-tools/types/index.test-d.ts

@@ -0,0 +1 @@
+import ReduxDevTools = require('../')

+ 12 - 14
packages/@uppy/status-bar/types/index.d.ts

@@ -1,21 +1,19 @@
-import Uppy = require('@uppy/core');
+import Uppy = require('@uppy/core')
+import GeneratedLocale = require('./generatedLocale')
 
 
 declare module StatusBar {
 declare module StatusBar {
+  export type StatusBarLocale = GeneratedLocale
+
   export interface StatusBarOptions extends Uppy.PluginOptions {
   export interface StatusBarOptions extends Uppy.PluginOptions {
-    showProgressDetails: boolean;
-    hideUploadButton: boolean;
-    hideAfterFinish: boolean;
+    replaceTargetContent?: boolean
+    target?: Uppy.PluginTarget
+    showProgressDetails?: boolean
+    hideUploadButton?: boolean
+    hideAfterFinish?: boolean
+    locale?: StatusBarLocale
   }
   }
 }
 }
 
 
-declare class StatusBar extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<StatusBar.StatusBarOptions>);
-}
-
-export = StatusBar;
+declare class StatusBar extends Uppy.Plugin<StatusBar.StatusBarOptions> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof StatusBar, opts: Partial<StatusBar.StatusBarOptions>): Uppy.Uppy;
-  }
-}
+export = StatusBar

+ 2 - 0
packages/@uppy/status-bar/types/index.test-d.ts

@@ -0,0 +1,2 @@
+import StatusBar = require('../')
+// TODO implement

+ 7 - 7
packages/@uppy/store-default/types/index.d.ts

@@ -1,11 +1,11 @@
-import UppyUtils = require('@uppy/utils');
+import UppyUtils = require('@uppy/utils')
 
 
 declare class DefaultStore implements UppyUtils.Store {
 declare class DefaultStore implements UppyUtils.Store {
-  constructor();
-  getState(): object;
-  setState(patch: object): void;
-  subscribe(listener: any): () => void;
+  constructor ()
+  getState (): object
+  setState (patch: object): void
+  subscribe (listener: any): () => void
 }
 }
 
 
-declare function createDefaultStore(): DefaultStore;
-export = createDefaultStore;
+declare function createDefaultStore (): DefaultStore
+export = createDefaultStore

+ 6 - 0
packages/@uppy/store-default/types/index.test-d.ts

@@ -0,0 +1,6 @@
+import DefaultStore = require('../')
+
+const store = DefaultStore()
+
+store.setState({ a: 'b' })
+store.getState()

+ 0 - 6
packages/@uppy/store-default/types/store-default-tests.ts

@@ -1,6 +0,0 @@
-import DefaultStore = require('../');
-
-const store = DefaultStore();
-
-store.setState({ a: 'b' });
-store.getState();

+ 16 - 14
packages/@uppy/store-redux/types/index.d.ts

@@ -1,24 +1,26 @@
-import UppyUtils = require('@uppy/utils');
-import { Reducer, Middleware, Store as Redux } from 'redux';
+import UppyUtils = require('@uppy/utils')
+import { Reducer, Middleware, Store as Redux } from 'redux'
 
 
 declare namespace ReduxStore {
 declare namespace ReduxStore {
   interface ReduxStoreOptions {
   interface ReduxStoreOptions {
-    store: Redux<object>;
-    id?: string;
-    selector?: (state: any) => object;
+    store: Redux<object>
+    id?: string
+    selector?: (state: any) => object
   }
   }
 
 
   interface ReduxStore extends UppyUtils.Store {
   interface ReduxStore extends UppyUtils.Store {
-    constructor(opts: ReduxStoreOptions): ReduxStore;
-    getState(): object;
-    setState(patch: object): void;
-    subscribe(listener: any): () => void;
+    constructor (opts: ReduxStoreOptions): ReduxStore
+    getState (): object
+    setState (patch: object): void
+    subscribe (listener: any): () => void
   }
   }
 
 
-  const reducer: Reducer<object>;
-  const middleware: Middleware;
-  const STATE_UPDATE: string;
+  const reducer: Reducer<object>
+  const middleware: Middleware
+  const STATE_UPDATE: string
 }
 }
-declare function ReduxStore(opts: ReduxStore.ReduxStoreOptions): ReduxStore.ReduxStore;
+declare function ReduxStore (
+  opts: ReduxStore.ReduxStoreOptions
+): ReduxStore.ReduxStore
 
 
-export = ReduxStore;
+export = ReduxStore

+ 13 - 0
packages/@uppy/store-redux/types/index.test-d.ts

@@ -0,0 +1,13 @@
+import { createStore, combineReducers } from 'redux'
+import ReduxStore = require('../')
+
+const reducer = combineReducers({
+  uppy: ReduxStore.reducer
+})
+
+const store = ReduxStore({
+  store: createStore(reducer)
+})
+
+store.setState({ a: 1 })
+store.getState()

+ 0 - 13
packages/@uppy/store-redux/types/store-redux-tests.ts

@@ -1,13 +0,0 @@
-import { createStore, combineReducers } from 'redux';
-import ReduxStore = require('../');
-
-const reducer = combineReducers({
-  uppy: ReduxStore.reducer
-});
-
-const store = ReduxStore({
-  store: createStore(reducer)
-});
-
-store.setState({ a: 1 });
-store.getState();

+ 8 - 12
packages/@uppy/thumbnail-generator/types/index.d.ts

@@ -1,19 +1,15 @@
-import Uppy = require('@uppy/core');
+import Uppy = require('@uppy/core')
+import ThumbnailGeneratorLocale = require('./generatedLocale')
 
 
 declare module ThumbnailGenerator {
 declare module ThumbnailGenerator {
   interface ThumbnailGeneratorOptions extends Uppy.PluginOptions {
   interface ThumbnailGeneratorOptions extends Uppy.PluginOptions {
-    thumbnailWidth: number;
+    thumbnailWidth?: number
+    locale?: ThumbnailGeneratorLocale
   }
   }
 }
 }
 
 
-declare class ThumbnailGenerator extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<ThumbnailGenerator.ThumbnailGeneratorOptions>);
-}
-
-export = ThumbnailGenerator;
+declare class ThumbnailGenerator extends Uppy.Plugin<
+  ThumbnailGenerator.ThumbnailGeneratorOptions
+> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof ThumbnailGenerator, opts: Partial<ThumbnailGenerator.ThumbnailGeneratorOptions>): Uppy.Uppy;
-  }
-}
+export = ThumbnailGenerator

+ 2 - 0
packages/@uppy/thumbnail-generator/types/index.test-d.ts

@@ -0,0 +1,2 @@
+import ThumbnailGenerator = require('../')
+// TODO implement

+ 30 - 27
packages/@uppy/transloadit/types/index.d.ts

@@ -1,40 +1,43 @@
-import Uppy = require('@uppy/core');
+import Uppy = require('@uppy/core')
+import TransloaditLocale = require('./generatedLocale')
 
 
 declare module Transloadit {
 declare module Transloadit {
   interface AssemblyParameters {
   interface AssemblyParameters {
-    auth: { key: string };
-    template_id?: string;
-    steps?: { [step: string]: object };
-    notify_url?: string;
-    fields?: { [name: string]: number | string };
+    auth: { key: string }
+    template_id?: string
+    steps?: { [step: string]: object }
+    notify_url?: string
+    fields?: { [name: string]: number | string }
   }
   }
 
 
   interface AssemblyOptions {
   interface AssemblyOptions {
-    params: AssemblyParameters;
-    fields?: { [name: string]: number | string };
-    signature?: string;
+    params: AssemblyParameters
+    fields?: { [name: string]: number | string }
+    signature?: string
   }
   }
 
 
-  interface TransloaditOptions extends Uppy.PluginOptions {
-    params: AssemblyParameters;
-    signature: string;
-    service: string;
-    waitForEncoding: boolean;
-    waitForMetadata: boolean;
-    importFromUploadURLs: boolean;
-    alwaysRunAssembly: boolean;
-    getAssemblyOptions: (file: Uppy.UppyFile) => AssemblyOptions | Promise<AssemblyOptions>;
+  interface TransloaditOptionsBase extends Uppy.PluginOptions {
+    service?: string
+    errorReporting?: boolean
+    waitForEncoding?: boolean
+    waitForMetadata?: boolean
+    importFromUploadURLs?: boolean
+    alwaysRunAssembly?: boolean
+    locale?: TransloaditLocale
+    limit?: number
   }
   }
-}
 
 
-declare class Transloadit extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<Transloadit.TransloaditOptions>);
+  // Either have a getAssemblyOptions() that returns an AssemblyOptions, *or* have them embedded in the options
+  type TransloaditOptions = TransloaditOptionsBase &
+    (
+      | {
+          getAssemblyOptions?: (
+            file: Uppy.UppyFile
+          ) => AssemblyOptions | Promise<AssemblyOptions>
+        }
+      | AssemblyOptions)
 }
 }
 
 
-export = Transloadit;
+declare class Transloadit extends Uppy.Plugin<Transloadit.TransloaditOptions> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof Transloadit, opts: Partial<Transloadit.TransloaditOptions>): Uppy.Uppy;
-  }
-}
+export = Transloadit

+ 63 - 0
packages/@uppy/transloadit/types/index.test-d.ts

@@ -0,0 +1,63 @@
+import { expectError, expectType } from 'tsd'
+import Uppy = require('@uppy/core')
+import Transloadit = require('../')
+
+const validParams = {
+  auth: { key: 'not so secret key' }
+}
+
+{
+  const uppy = Uppy<Uppy.StrictTypes>()
+  uppy.use(Transloadit, {
+    getAssemblyOptions (file) {
+      expectType<Uppy.UppyFile>(file)
+      return { params: validParams }
+    },
+    waitForEncoding: false,
+    waitForMetadata: true,
+    importFromUploadURLs: false,
+    params: {
+      auth: { key: 'abc' },
+      steps: {}
+    }
+  })
+}
+
+{
+  const uppy = Uppy<Uppy.StrictTypes>()
+  // must be bools
+  expectError(
+    uppy.use(Transloadit, { waitForEncoding: null, params: validParams })
+  )
+  expectError(
+    uppy.use(Transloadit, { waitForMetadata: null, params: validParams })
+  )
+}
+
+{
+  const uppy = Uppy<Uppy.StrictTypes>()
+  // params.auth.key must be string
+  expectError(uppy.use(Transloadit, { params: {} }))
+  expectError(uppy.use(Transloadit, { params: { auth: {} } }))
+  expectError(
+    uppy.use(Transloadit, {
+      params: {
+        auth: { key: null }
+      }
+    })
+  )
+  expectError(
+    uppy.use(Transloadit, {
+      params: {
+        auth: { key: 'abc' },
+        steps: 'test'
+      }
+    })
+  )
+  uppy.use(Transloadit, {
+    params: {
+      auth: { key: 'abc' },
+      steps: { name: {} }
+    }
+  })
+}

+ 0 - 53
packages/@uppy/transloadit/types/transloadit-tests.ts

@@ -1,53 +0,0 @@
-import Uppy = require('@uppy/core')
-import Transloadit = require('../')
-
-{
-  const uppy = Uppy()
-  uppy.use(Transloadit, {
-    getAssemblyOptions(file) {
-      file // $ExpectType Uppy.UppyFile
-    },
-    waitForEncoding: false,
-    waitForMetadata: true,
-    importFromUploadURLs: false,
-    params: {
-      auth: { key: 'abc' },
-      steps: {}
-    }
-  })
-}
-
-{
-  const uppy = Uppy()
-  // $ExpectError
-  uppy.use(Transloadit, { waitForEncoding: null })
-  // $ExpectError
-  uppy.use(Transloadit, { waitForMetadata: null })
-}
-
-{
-  const uppy = Uppy()
-  // $ExpectError
-  uppy.use(Transloadit, { params: {} })
-  // $ExpectError
-  uppy.use(Transloadit, { params: { auth: {} } })
-  // $ExpectError
-  uppy.use(Transloadit, {
-    params: {
-      auth: { key: null }
-    }
-  })
-  // $ExpectError
-  uppy.use(Transloadit, {
-    params: {
-      auth: { key: 'abc' },
-      steps: 'test'
-    }
-  })
-  uppy.use(Transloadit, {
-    params: {
-      auth: { key: 'abc' },
-      steps: { name: {} }
-    }
-  })
-}

+ 1 - 0
packages/@uppy/tus/package.json

@@ -22,6 +22,7 @@
     "url": "git+https://github.com/transloadit/uppy.git"
     "url": "git+https://github.com/transloadit/uppy.git"
   },
   },
   "dependencies": {
   "dependencies": {
+    "@types/tus-js-client": "^1.8.0",
     "@uppy/companion-client": "file:../companion-client",
     "@uppy/companion-client": "file:../companion-client",
     "@uppy/utils": "file:../utils",
     "@uppy/utils": "file:../utils",
     "tus-js-client": "^1.8.0"
     "tus-js-client": "^1.8.0"

+ 19 - 23
packages/@uppy/tus/types/index.d.ts

@@ -1,29 +1,25 @@
-import Uppy = require('@uppy/core');
+import Uppy = require('@uppy/core')
+import { UploadOptions } from 'tus-js-client'
 
 
 declare module Tus {
 declare module Tus {
-  export interface TusOptions extends Uppy.PluginOptions {
-    resume: boolean;
-    removeFingerprintOnSuccess: boolean;
-    endpoint: string;
-    headers: object;
-    chunkSize: number;
-    withCredentials: boolean;
-    overridePatchMethod: boolean;
-    retryDelays: number[];
-    metaFields: string[] | null;
-    autoRetry: boolean;
-    limit: number;
-  }
-}
+  type TusUploadOptions = Pick<UploadOptions, Exclude<keyof UploadOptions,
+    | 'fingerprint'
+    | 'metadata'
+    | 'onProgress'
+    | 'onChunkComplete'
+    | 'onSuccess'
+    | 'onError'
+    | 'uploadUrl'
+    | 'uploadSize'
+  >>
 
 
-declare class Tus extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<Tus.TusOptions>);
+  export interface TusOptions extends Uppy.PluginOptions, TusUploadOptions {
+    metaFields?: string[] | null
+    autoRetry?: boolean
+    limit?: number
+  }
 }
 }
 
 
-export = Tus;
+declare class Tus extends Uppy.Plugin<Tus.TusOptions> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof Tus, opts: Partial<Tus.TusOptions>): Uppy.Uppy;
-  }
-}
+export = Tus

+ 1 - 0
packages/@uppy/tus/types/index.test-d.ts

@@ -0,0 +1 @@
+import Tus = require('../')

+ 12 - 14
packages/@uppy/url/types/index.d.ts

@@ -1,20 +1,18 @@
-import Uppy = require('@uppy/core');
+import Uppy = require('@uppy/core')
+import CompanionClient = require('@uppy/companion-client')
+import UrlLocale = require('./generatedLocale')
 
 
 declare module Url {
 declare module Url {
-  export interface UrlOptions extends Uppy.PluginOptions {
-    companionUrl: string;
-    // TODO inherit from ProviderOptions
+  export interface UrlOptions
+    extends Uppy.PluginOptions,
+      CompanionClient.RequestClientOptions {
+    replaceTargetContent?: boolean
+    target?: Uppy.PluginTarget
+    title?: string
+    locale?: UrlLocale
   }
   }
 }
 }
 
 
-declare class Url extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<Url.UrlOptions>);
-}
-
-export = Url;
+declare class Url extends Uppy.Plugin<Url.UrlOptions> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof Url, opts: Partial<Url.UrlOptions>): Uppy.Uppy;
-  }
-}
+export = Url

+ 2 - 0
packages/@uppy/url/types/index.test-d.ts

@@ -0,0 +1,2 @@
+import Url = require('../')
+// TODO implement

+ 117 - 85
packages/@uppy/utils/types/index.d.ts

@@ -3,15 +3,15 @@ declare module '@uppy/utils/lib/Translator' {
     export interface TranslatorOptions {
     export interface TranslatorOptions {
       locale: {
       locale: {
         strings: {
         strings: {
-          [key: string]: string | { [plural: number]: string };
-        };
-        pluralize: (n: number) => number;
-      };
+          [key: string]: string | { [plural: number]: string }
+        }
+        pluralize: (n: number) => number
+      }
     }
     }
   }
   }
 
 
   class Translator {
   class Translator {
-    constructor(opts: Translator.TranslatorOptions);
+    constructor (opts: Translator.TranslatorOptions)
   }
   }
 
 
   export = Translator
   export = Translator
@@ -19,17 +19,17 @@ declare module '@uppy/utils/lib/Translator' {
 
 
 declare module '@uppy/utils/lib/EventTracker' {
 declare module '@uppy/utils/lib/EventTracker' {
   namespace EventTracker {
   namespace EventTracker {
-    export type EventHandler = (...args: any[]) => void;
+    export type EventHandler = (...args: any[]) => void
     export interface Emitter {
     export interface Emitter {
-      on: (event: string, handler: EventHandler) => void;
-      off: (event: string, handler: EventHandler) => void;
+      on: (event: string, handler: EventHandler) => void
+      off: (event: string, handler: EventHandler) => void
     }
     }
   }
   }
 
 
   class EventTracker {
   class EventTracker {
-    constructor(emitter: EventTracker.Emitter);
-    on(event: string, handler: EventTracker.EventHandler): void;
-    remove(): void;
+    constructor (emitter: EventTracker.Emitter)
+    on (event: string, handler: EventTracker.EventHandler): void
+    remove (): void
   }
   }
 
 
   export = EventTracker
   export = EventTracker
@@ -37,208 +37,240 @@ declare module '@uppy/utils/lib/EventTracker' {
 
 
 declare module '@uppy/utils/lib/ProgressTimeout' {
 declare module '@uppy/utils/lib/ProgressTimeout' {
   class ProgressTimeout {
   class ProgressTimeout {
-    constructor(timeout: number, timeoutHandler: () => void);
-    progress(): void;
-    done(): void;
+    constructor (timeout: number, timeoutHandler: () => void)
+    progress (): void
+    done (): void
   }
   }
   export = ProgressTimeout
   export = ProgressTimeout
 }
 }
 
 
 declare module '@uppy/utils/lib/RateLimitedQueue' {
 declare module '@uppy/utils/lib/RateLimitedQueue' {
   namespace RateLimitedQueue {
   namespace RateLimitedQueue {
-    export type AbortFunction = () => void;
-    export type PromiseFunction = (...args: any[]) => Promise<any>;
+    export type AbortFunction = () => void
+    export type PromiseFunction = (...args: any[]) => Promise<any>
     export type QueueEntry = {
     export type QueueEntry = {
-      abort: () => void,
-      done: () => void,
-    };
+      abort: () => void
+      done: () => void
+    }
   }
   }
 
 
   class RateLimitedQueue {
   class RateLimitedQueue {
-    constructor(limit: number);
-    run(fn: () => RateLimitedQueue.AbortFunction): RateLimitedQueue.QueueEntry;
-    wrapPromiseFunction(fn: () => RateLimitedQueue.PromiseFunction): RateLimitedQueue.PromiseFunction;
+    constructor (limit: number)
+    run (fn: () => RateLimitedQueue.AbortFunction): RateLimitedQueue.QueueEntry
+    wrapPromiseFunction(
+      fn: () => RateLimitedQueue.PromiseFunction
+    ): RateLimitedQueue.PromiseFunction
   }
   }
 
 
   export = RateLimitedQueue
   export = RateLimitedQueue
 }
 }
 
 
 declare module '@uppy/utils/lib/canvasToBlob' {
 declare module '@uppy/utils/lib/canvasToBlob' {
-  function canvasToBlob(canvas: HTMLCanvasElement, type: string, quality?: number): Promise<Blob>;
+  function canvasToBlob (
+    canvas: HTMLCanvasElement,
+    type: string,
+    quality?: number
+  ): Promise<Blob>
   export = canvasToBlob
   export = canvasToBlob
 }
 }
 
 
 declare module '@uppy/utils/lib/dataURItoBlob' {
 declare module '@uppy/utils/lib/dataURItoBlob' {
-  function dataURItoBlob(dataURI: string, opts: { mimeType?: string, name?: string }): Blob;
+  function dataURItoBlob (
+    dataURI: string,
+    opts: { mimeType?: string; name?: string }
+  ): Blob
   export = dataURItoBlob
   export = dataURItoBlob
 }
 }
 
 
 declare module '@uppy/utils/lib/dataURItoFile' {
 declare module '@uppy/utils/lib/dataURItoFile' {
-  function dataURItoFile(dataURI: string, opts: { mimeType?: string, name?: string }): File;
+  function dataURItoFile (
+    dataURI: string,
+    opts: { mimeType?: string; name?: string }
+  ): File
   export = dataURItoFile
   export = dataURItoFile
 }
 }
 
 
 declare module '@uppy/utils/lib/emitSocketProgress' {
 declare module '@uppy/utils/lib/emitSocketProgress' {
-  import UppyUtils = require('@uppy/utils');
+  import UppyUtils = require('@uppy/utils')
 
 
   interface ProgressData {
   interface ProgressData {
-    progress: number;
-    bytesUploaded: number;
-    bytesTotal: number;
+    progress: number
+    bytesUploaded: number
+    bytesTotal: number
   }
   }
 
 
-  function emitSocketProgress(uploader: object, progressData: ProgressData, file: UppyUtils.UppyFile): void;
+  function emitSocketProgress (
+    uploader: object,
+    progressData: ProgressData,
+    file: UppyUtils.UppyFile
+  ): void
   export = emitSocketProgress
   export = emitSocketProgress
 }
 }
 
 
 declare module '@uppy/utils/lib/findAllDOMElements' {
 declare module '@uppy/utils/lib/findAllDOMElements' {
-  function findAllDOMElements(element: string | HTMLElement): HTMLElement[];
+  function findAllDOMElements (element: string | HTMLElement): HTMLElement[]
   export = findAllDOMElements
   export = findAllDOMElements
 }
 }
 
 
 declare module '@uppy/utils/lib/findDOMElement' {
 declare module '@uppy/utils/lib/findDOMElement' {
-  function findDOMElement(element: string | HTMLElement): HTMLElement | null;
+  function findDOMElement (element: string | HTMLElement): HTMLElement | null
   export = findDOMElement
   export = findDOMElement
 }
 }
 
 
 declare module '@uppy/utils/lib/generateFileID' {
 declare module '@uppy/utils/lib/generateFileID' {
-  import UppyUtils = require('@uppy/utils');
+  import UppyUtils = require('@uppy/utils')
 
 
-  function generateFileID(file: UppyUtils.UppyFile): string;
+  function generateFileID (file: UppyUtils.UppyFile): string
   export = generateFileID
   export = generateFileID
 }
 }
 
 
 declare module '@uppy/utils/lib/getBytesRemaining' {
 declare module '@uppy/utils/lib/getBytesRemaining' {
-  function getBytesRemaining(progress: { bytesTotal: number, bytesUploaded: number }): number;
+  function getBytesRemaining (progress: {
+    bytesTotal: number
+    bytesUploaded: number
+  }): number
   export = getBytesRemaining
   export = getBytesRemaining
 }
 }
 
 
 declare module '@uppy/utils/lib/getETA' {
 declare module '@uppy/utils/lib/getETA' {
-  function getETA(progress: object): number;
+  function getETA (progress: object): number
   export = getETA
   export = getETA
 }
 }
 
 
 declare module '@uppy/utils/lib/getFileNameAndExtension' {
 declare module '@uppy/utils/lib/getFileNameAndExtension' {
-  function getFileNameAndExtension(filename: string): { name: string, extension: string | undefined };
+  function getFileNameAndExtension(
+    filename: string
+  ): { name: string, extension: string | undefined };
   export = getFileNameAndExtension
   export = getFileNameAndExtension
 }
 }
 
 
 declare module '@uppy/utils/lib/getFileType' {
 declare module '@uppy/utils/lib/getFileType' {
-  import UppyUtils = require('@uppy/utils');
+  import UppyUtils = require('@uppy/utils')
 
 
-  function getFileType(file: UppyUtils.UppyFile): string | null;
+  function getFileType (file: UppyUtils.UppyFile): string | null
   export = getFileType
   export = getFileType
 }
 }
 
 
 declare module '@uppy/utils/lib/getFileTypeExtension' {
 declare module '@uppy/utils/lib/getFileTypeExtension' {
-  function getFileTypeExtension(mime: string): string;
+  function getFileTypeExtension (mime: string): string
   export = getFileTypeExtension
   export = getFileTypeExtension
 }
 }
 
 
 declare module '@uppy/utils/lib/getSocketHost' {
 declare module '@uppy/utils/lib/getSocketHost' {
-  function getSocketHost(url: string): string;
+  function getSocketHost (url: string): string
   export = getSocketHost
   export = getSocketHost
 }
 }
 
 
 declare module '@uppy/utils/lib/getSpeed' {
 declare module '@uppy/utils/lib/getSpeed' {
-  function getSpeed(progress: { bytesTotal: number, bytesUploaded: number }): number;
+  function getSpeed (progress: {
+    bytesTotal: number
+    bytesUploaded: number
+  }): number
   export = getSpeed
   export = getSpeed
 }
 }
 
 
 declare module '@uppy/utils/lib/getTimeStamp' {
 declare module '@uppy/utils/lib/getTimeStamp' {
-  function getTimeStamp(): string;
+  function getTimeStamp (): string
   export = getTimeStamp
   export = getTimeStamp
 }
 }
 
 
 declare module '@uppy/utils/lib/isDOMElement' {
 declare module '@uppy/utils/lib/isDOMElement' {
-  function isDOMElement(element: any): boolean;
+  function isDOMElement (element: any): boolean
   export = isDOMElement
   export = isDOMElement
 }
 }
 
 
 declare module '@uppy/utils/lib/isObjectURL' {
 declare module '@uppy/utils/lib/isObjectURL' {
-  function isObjectURL(url: string): boolean;
+  function isObjectURL (url: string): boolean
   export = isObjectURL
   export = isObjectURL
 }
 }
 
 
 declare module '@uppy/utils/lib/isDragDropSupported' {
 declare module '@uppy/utils/lib/isDragDropSupported' {
-  function isDragDropSupported(): boolean;
+  function isDragDropSupported (): boolean
   export = isDragDropSupported
   export = isDragDropSupported
 }
 }
 
 
 declare module '@uppy/utils/lib/isPreviewSupported' {
 declare module '@uppy/utils/lib/isPreviewSupported' {
-  function isPreviewSupported(mime: string): boolean;
+  function isPreviewSupported (mime: string): boolean
   export = isPreviewSupported
   export = isPreviewSupported
 }
 }
 
 
 declare module '@uppy/utils/lib/isTouchDevice' {
 declare module '@uppy/utils/lib/isTouchDevice' {
-  function isTouchDevice(): boolean;
+  function isTouchDevice (): boolean
   export = isTouchDevice
   export = isTouchDevice
 }
 }
 
 
 declare module '@uppy/utils/lib/prettyETA' {
 declare module '@uppy/utils/lib/prettyETA' {
-  function prettyETA(seconds: number): string;
+  function prettyETA (seconds: number): string
   export = prettyETA
   export = prettyETA
 }
 }
 
 
 declare module '@uppy/utils/lib/secondsToTime' {
 declare module '@uppy/utils/lib/secondsToTime' {
-  function secondsToTime(seconds: number): string;
+  function secondsToTime (seconds: number): string
   export = secondsToTime
   export = secondsToTime
 }
 }
 
 
 declare module '@uppy/utils/lib/settle' {
 declare module '@uppy/utils/lib/settle' {
-  function settle<T>(promises: Promise<T>[]): Promise<{ successful: T[], failed: any[] }>;
+  function settle<T> (
+    promises: Promise<T>[]
+  ): Promise<{ successful: T[]; failed: any[] }>
   export = settle
   export = settle
 }
 }
 
 
 declare module '@uppy/utils/lib/toArray' {
 declare module '@uppy/utils/lib/toArray' {
-  function toArray(list: any): any[];
+  function toArray (list: any): any[]
   export = toArray
   export = toArray
 }
 }
 
 
 declare module '@uppy/utils/lib/getDroppedFiles' {
 declare module '@uppy/utils/lib/getDroppedFiles' {
-  function getDroppedFiles(dataTransfer: DataTransfer, options?: object): Promise<File[]>;
+  function getDroppedFiles (
+    dataTransfer: DataTransfer,
+    options?: object
+  ): Promise<File[]>
   export = getDroppedFiles
   export = getDroppedFiles
 }
 }
 
 
 declare module '@uppy/utils' {
 declare module '@uppy/utils' {
   interface IndexedObject<T> {
   interface IndexedObject<T> {
-    [key: string]: T;
-    [key: number]: T;
+    [key: string]: T
+    [key: number]: T
   }
   }
-  export type InternalMetadata = { name: string, type?: string };
-  export interface UppyFile<TMeta = IndexedObject<any>, TBody = IndexedObject<any>> {
-    data: Blob | File;
-    extension: string;
-    id: string;
-    isPaused?: boolean;
-    isRemote: boolean;
-    meta: InternalMetadata & TMeta;
-    name: string;
-    preview?: string;
+  export type InternalMetadata = { name: string; type?: string }
+  export interface UppyFile<
+    TMeta = IndexedObject<any>,
+    TBody = IndexedObject<any>
+  > {
+    data: Blob | File
+    extension: string
+    id: string
+    isPaused?: boolean
+    isRemote: boolean
+    meta: InternalMetadata & TMeta
+    name: string
+    preview?: string
     progress?: {
     progress?: {
-      uploadStarted: number | null;
-      uploadComplete: boolean;
-      percentage: number;
-      bytesUploaded: number;
-      bytesTotal: number;
-    };
+      uploadStarted: number | null
+      uploadComplete: boolean
+      percentage: number
+      bytesUploaded: number
+      bytesTotal: number
+    }
     remote?: {
     remote?: {
-      host: string;
-      url: string;
-      body?: object;
-    };
-    size: number;
-    source?: string;
-    type?: string;
+      host: string
+      url: string
+      body?: object
+    }
+    size: number
+    source?: string
+    type?: string
     response?: {
     response?: {
-      body: TBody;
-      status: number;
-      uploadURL: string | undefined;
-    };
+      body: TBody
+      status: number
+      uploadURL: string | undefined
+    }
   }
   }
   export interface Store {
   export interface Store {
-    getState(): object;
-    setState(patch: object): void;
-    subscribe(listener: any): () => void;
+    getState (): object
+    setState (patch: object): void
+    subscribe (listener: any): () => void
   }
   }
 }
 }

+ 1 - 0
packages/@uppy/utils/types/index.test-d.ts

@@ -0,0 +1 @@
+// TODO implement

+ 18 - 17
packages/@uppy/webcam/types/index.d.ts

@@ -1,25 +1,26 @@
-import Uppy = require('@uppy/core');
+import Uppy = require('@uppy/core')
+import WebcamLocale = require('./generatedLocale')
 
 
 declare module Webcam {
 declare module Webcam {
-  export type WebcamMode = 'video-audio' | 'video-only' | 'audio-only' | 'picture';
+  export type WebcamMode =
+    | 'video-audio'
+    | 'video-only'
+    | 'audio-only'
+    | 'picture'
 
 
   export interface WebcamOptions extends Uppy.PluginOptions {
   export interface WebcamOptions extends Uppy.PluginOptions {
-    onBeforeSnapshot?: () => Promise<void>;
-    countdown?: number | boolean;
-    mirror?: boolean;
-    facingMode?: string;
-    modes: WebcamMode[];
+    replaceTargetContent?: boolean
+    target?: Uppy.PluginTarget
+    onBeforeSnapshot?: () => Promise<void>
+    countdown?: number | boolean
+    mirror?: boolean
+    facingMode?: string
+    modes?: WebcamMode[]
+    locale?: WebcamLocale
+    title?: string
   }
   }
 }
 }
 
 
-declare class Webcam extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: Partial<Webcam.WebcamOptions>);
-}
-
-export = Webcam;
+declare class Webcam extends Uppy.Plugin<Webcam.WebcamOptions> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof Webcam, opts: Partial<Webcam.WebcamOptions>): Uppy.Uppy;
-  }
-}
+export = Webcam

+ 8 - 0
packages/@uppy/webcam/types/index.test-d.ts

@@ -0,0 +1,8 @@
+import Uppy = require('@uppy/core')
+import Webcam = require('../')
+
+{
+  Uppy<Uppy.StrictTypes>().use(Webcam, {
+    modes: ['video-only']
+  })
+}

+ 0 - 8
packages/@uppy/webcam/types/webcam-tests.ts

@@ -1,8 +0,0 @@
-import Uppy = require('@uppy/core');
-import Webcam = require('../');
-
-{
-  Uppy().use(Webcam, {
-    modes: ['video-only']
-  });
-}

+ 15 - 21
packages/@uppy/xhr-upload/types/index.d.ts

@@ -1,28 +1,22 @@
-import Uppy = require('@uppy/core');
+import Uppy = require('@uppy/core')
+import XHRUploadLocale = require('./generatedLocale')
 
 
 declare module XHRUpload {
 declare module XHRUpload {
   export interface XHRUploadOptions extends Uppy.PluginOptions {
   export interface XHRUploadOptions extends Uppy.PluginOptions {
-    limit?: number;
-    bundle?: boolean;
-    formData?: FormData;
-    headers?: any;
-    metaFields?: string[];
-    fieldName?: string;
-    timeout?: number;
-    responseUrlFieldName?: string;
-    endpoint: string;
-    method?: 'GET' | 'POST' | 'PUT' | 'HEAD';
+    limit?: number
+    bundle?: boolean
+    formData?: boolean
+    headers?: any
+    metaFields?: string[]
+    fieldName?: string
+    timeout?: number
+    responseUrlFieldName?: string
+    endpoint: string
+    method?: 'GET' | 'POST' | 'PUT' | 'HEAD' | 'get' | 'post' | 'put' | 'head'
+    locale?: XHRUploadLocale
   }
   }
 }
 }
 
 
-declare class XHRUpload extends Uppy.Plugin {
-  constructor(uppy: Uppy.Uppy, opts: XHRUpload.XHRUploadOptions);
-}
-
-export = XHRUpload;
+declare class XHRUpload extends Uppy.Plugin<XHRUpload.XHRUploadOptions> {}
 
 
-declare module '@uppy/core' {
-  export interface Uppy {
-    use(pluginClass: typeof XHRUpload, opts: XHRUpload.XHRUploadOptions): Uppy.Uppy;
-  }
-}
+export = XHRUpload

+ 19 - 0
packages/@uppy/xhr-upload/types/index.test-d.ts

@@ -0,0 +1,19 @@
+import Uppy = require('@uppy/core')
+import XHRUpload = require('../')
+
+Uppy<Uppy.StrictTypes>().use(XHRUpload, {
+  bundle: false,
+  formData: true,
+  endpoint: 'xyz'
+})
+
+function methodMayBeUpperOrLowerCase () {
+  Uppy<Uppy.StrictTypes>().use(XHRUpload, {
+    endpoint: '/upload',
+    method: 'post'
+  })
+  Uppy<Uppy.StrictTypes>().use(XHRUpload, {
+    endpoint: '/upload',
+    method: 'PUT'
+  })
+}

+ 0 - 9
packages/@uppy/xhr-upload/types/xhr-upload-tests.ts

@@ -1,9 +0,0 @@
-import Uppy = require('@uppy/core');
-import XHRUpload = require('../');
-
-{
-  Uppy().use(XHRUpload, {
-    bundle: false,
-    endpoint: 'xyz'
-  } as XHRUpload.XHRUploadOptions);
-}

+ 0 - 0
packages/uppy/types/uppy-tests.ts → packages/uppy/types/index.test-d.ts


+ 1 - 1
test/endtoend/typescript/main.ts

@@ -16,7 +16,7 @@ import {
 const isOnTravis = !!(process.env.TRAVIS && process.env.CI)
 const isOnTravis = !!(process.env.TRAVIS && process.env.CI)
 const TUS_ENDPOINT = `http://${isOnTravis ? 'companion.test' : 'localhost'}:1080/files/`
 const TUS_ENDPOINT = `http://${isOnTravis ? 'companion.test' : 'localhost'}:1080/files/`
 
 
-const uppy = Core({
+const uppy = Core<Core.StrictTypes>({
   debug: true,
   debug: true,
   meta: {
   meta: {
     username: 'John',
     username: 'John',

+ 1 - 3
tsconfig.json

@@ -20,9 +20,7 @@
   },
   },
   "include": [
   "include": [
     "packages/*/types/index.d.ts",
     "packages/*/types/index.d.ts",
-    "packages/*/types/*-tests.ts",
-    "packages/@uppy/*/types/index.d.ts",
-    "packages/@uppy/*/types/*-tests.ts"
+    "packages/@uppy/*/types/index.d.ts"
   ],
   ],
   "exclude": [
   "exclude": [
     "packages/@uppy/companion"
     "packages/@uppy/companion"

+ 33 - 0
website/src/docs/uppy.md

@@ -30,6 +30,39 @@ In the [CDN package](/docs/#With-a-script-tag), it is available on the `Uppy` gl
 const Core = Uppy.Core
 const Core = Uppy.Core
 ```
 ```
 
 
+## TypeScript
+
+When using TypeScript, Uppy has weak type checking by default. That means that the options to plugins are not type-checked. For example, this is allowed:
+```ts
+import Uppy = require('@uppy/core')
+import Tus = require('@uppy/tus')
+const uppy = Uppy()
+uppy.use(Tus, {
+  invalidOption: null,
+  endpoint: ['a', 'wrong', 'type']
+})
+```
+
+As of Uppy 1.10, Uppy supports a strict typechecking mode. This mode typechecks the options passed in to plugins. This will be the only mode in Uppy 2.0, but is currently optional to preserve backwards compatibility. The strict mode can be enabled by passing a special generic type parameter to the Uppy constructor:
+
+```ts
+import Uppy = require('@uppy/core')
+import Tus = require('@uppy/tus')
+const uppy = Uppy<Uppy.StrictTypes>()
+uppy.use(Tus, {
+  invalidOption: null // this will now make the compilation fail!
+})
+```
+
+If you are storing Uppy instances in your code, for example in a property on a React or Angular component class, make sure to add the `StrictTypes` flag there as well:
+```ts
+class MyComponent extends React.Component {
+  private uppy: Uppy<Uppy.StrictTypes>
+}
+```
+
+In Uppy 2.0, this generic parameter will be removed, and your plugin options will always be type-checked.
+
 ## Options
 ## Options
 
 
 The Uppy core module has the following configurable options:
 The Uppy core module has the following configurable options:

Some files were not shown because too many files changed in this diff