diff --git a/types/navigator-permissions/index.d.ts b/types/navigator-permissions/index.d.ts new file mode 100644 index 0000000000..a62c7f77ef --- /dev/null +++ b/types/navigator-permissions/index.d.ts @@ -0,0 +1,180 @@ +// Type definitions for Navigator.Permissions 0.1 +// Project: https://developer.mozilla.org/en-US/docs/Web/API/Permissions +// Definitions by: Vince Varga , MindDoc +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.3 + +/** + * Namespace for `navigator.permissions` type definitions. + * + * As the Permissions API is only supported by Firefox and Chrome + * https://caniuse.com/#feat=permissions-api + * the TypeScript team has not yet added it to lib.dom.d.ts + * https://github.com/Microsoft/TypeScript/issues/24923 + * In the meantime, these type definitions can be used. + * + * The documentation is based on the MDN web docs + * https://developer.mozilla.org/en-US/docs/Web/API/Permissions + */ +declare namespace NavigatorPermissions { + /** + * Permission state values. + */ + type PermissionState = + 'granted' | + 'denied' | + 'prompt'; + + /** + * The `PermissionStatus` interface of the Permissions API provides the state + * of an object and an event handler for monitoring changes to said state. + * + * This is an experimental technology + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/PermissionStatus} + */ + interface PermissionStatus extends EventTarget { + /** + * Returns the state of a requested permission. + */ + readonly state: PermissionState; + /** + * Returns the state of a requested permission. + * Later versions of the specification replace this with + * `PermissionStatus.state`. + * @deprecated + */ + readonly status: PermissionState; + /** + * An event called whenever `PermissionStatus.status` changes. + */ + onchange: ((this: this, event: Event) => any) | null; + } + + /** + * Permission name options. + */ + type PermissionName = + 'accelerometer' | + 'accessibility-events' | + 'ambient-light-sensor' | + 'background-sync' | + 'camera' | + 'clipboard-read' | + 'clipboard-write' | + 'geolocation' | + 'gyroscope' | + 'magnetometer' | + 'microphone' | + 'midi' | + 'notifications' | + 'payment-handler' | + 'persistent-storage' | + 'push'; + + /** + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Permissions/query} + */ + interface PermissionDescriptor { + /** + * The name of the API whose permissions you want to query. + * + * Please, be aware, that not all of these are supported in every browser + * that supports the Permissions API. For example, in Firefox you can't query + * the `'microphone'` or `'camera'` and it'll throw `TypeError` + */ + name: N; + } + + interface PushPermissionDescriptor extends PermissionDescriptor<'push'> { + /** + * Indicates whether you want to show a notification for every message + * or be able to send silent push + * notifications. The default is `false`. Not supported in Firefox. + */ + userVisibleOnly?: boolean; + } + + interface MidiPermissionDescriptor extends PermissionDescriptor<'midi'> { + /** + * Indicates whether you need and/or receive system exclusive + * messages. The default is false. + */ + sysex?: boolean; + } + + // Map permission names to correctly typed descriptors. + interface NameDescriptorMap { + // ??? Question ???: + // Is there a better way to handle this case and remove repeated code, + // something like + // > { + // [n in N]: D; // this line to cover all basic cases + // // and the custom permission descriptors for midi and push + // } + 'accelerometer': PermissionDescriptor<'accelerometer'>; + 'accessibility-events': PermissionDescriptor<'accessibility-events'>; + 'ambient-light-sensor': PermissionDescriptor<'ambient-light-sensor'>; + 'background-sync': PermissionDescriptor<'background-sync'>; + 'camera': PermissionDescriptor<'camera'>; + 'clipboard-read': PermissionDescriptor<'clipboard-read'>; + 'clipboard-write': PermissionDescriptor<'clipboard-write'>; + 'geolocation': PermissionDescriptor<'geolocation'>; + 'gyroscope': PermissionDescriptor<'gyroscope'>; + 'magnetometer': PermissionDescriptor<'magnetometer'>; + 'microphone': PermissionDescriptor<'microphone'>; + 'notifications': PermissionDescriptor<'notifications'>; + 'payment-handler': PermissionDescriptor<'payment-handler'>; + 'persistent-storage': PermissionDescriptor<'persistent-storage'>; + // These permission descriptors support extra properties + 'midi': MidiPermissionDescriptor; + 'push': PushPermissionDescriptor; + } + + /** + * The `Permissions` interface of the Permissions API provides the core + * Permission API functionality, such as methods for querying and + * revoking permissions. + * + * This is an experimental technology. + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Permissions} + */ + interface Permissions { + /** + * The `Permissions.query()` method of the `Permissions` interface returns + * the state of a user permission on the global scope. + * @param permissionDescriptor object that sets + * options for the query operation consisting of a comma-separated list + * of name-value pairs. + * (Is comma-separated list really supported? It is mentioned in the docs, but does not work). + * @returns the user permission status for a given API. + * @throws `TypeError` Retrieving the `PermissionDescriptor` information + * failed in some way, or the permission doesn't exist or is currently + * unsupported (e.g. `midi`, or `push` with `userVisibleOnly`). + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Permissions/query} + */ + query(permissionDescriptor: NameDescriptorMap[keyof NameDescriptorMap]): Promise; + /** + * The `Permissions.revoke()` method of the `Permissions` interface reverts a + * currently set permission back to its default state, which is usually `prompt`. + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Permissions/revoke} + */ + revoke(permissionDescriptor: NameDescriptorMap[keyof NameDescriptorMap]): Promise; + } + + /** + * Navigator type definition with possible `permissions` API support. + * + * This interface adds the `permissions` property to the navigator. + */ + interface NavigatorPermissions extends Navigator { + /** + * Provides the core Permission API functionality, such as querying and revoking permissions. + * + * The typing takes into account that the feature is not widely supported, + * making code using this API more secure by forcing considering the `undefined` case. + */ + permissions?: Permissions; + } +} diff --git a/types/navigator-permissions/navigator-permissions-tests.ts b/types/navigator-permissions/navigator-permissions-tests.ts new file mode 100644 index 0000000000..918726b23d --- /dev/null +++ b/types/navigator-permissions/navigator-permissions-tests.ts @@ -0,0 +1,45 @@ +// Here are some examples for Navigator.Permissions types. +// Open it in your IDE with TypeScript support and observe the types and documentation help +// that this module provides + +// Navigator from lib.dom might have permissions +const nav = navigator as NavigatorPermissions.NavigatorPermissions; + +async function exampleQueryAndEventListeners() { + // Force users to check for undefined as the feature is not widely supported + if (typeof nav.permissions === 'undefined') { return; } + const permissionsStatus = await nav.permissions.query({ name: 'camera' }); + // Possible state values are known to users: + const isDenied = permissionsStatus.state === 'denied'; + // if compared to any non-valid value, it warns + // const tsShouldWarn = permissionsStatus.state === 'can you see the warning?'; + permissionsStatus.addEventListener('change', (event) => { + console.log('permission state changed'); + // I couldn't find a way to set the EventTarget's type to PermissionStatus + console.log('new PermissionStatus', event.target); + }); +} + +function exampleQueryOptions() { + // Force users to check for undefined as the feature is not widely supported + if (typeof nav.permissions === 'undefined') { return; } + // query method checks if the name is a valid permission name + nav.permissions.query({ name: 'camera' }); + // Other names would cause type errors + // nav.permissions.query({ name: 'invalid name' }); + + // When permission name is 'push', userVisibleOnly property is also supported + nav.permissions.query({ name: 'push', userVisibleOnly: true }); + // For other names, it would fail: + // nav.permissions.query({ name: 'camera', userVisibleOnly: true }); + + // When permission name is 'midi', sysex property is also supported + nav.permissions.query({ name: 'midi', sysex: true }); + // For other names, it would fail: + // nav.permissions.query({ name: 'camera', sysex: true }); +} + +function exampleIgnoreUndefinedCheck() { + // Using the ! after permissions will let you bypass the undefined-check + nav.permissions!.query({ name: 'microphone' }); +} diff --git a/types/navigator-permissions/tsconfig.json b/types/navigator-permissions/tsconfig.json new file mode 100644 index 0000000000..c072886b33 --- /dev/null +++ b/types/navigator-permissions/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6", + "dom" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "baseUrl": "../", + "typeRoots": [ + "../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "navigator-permissions-tests.ts" + ] +} diff --git a/types/navigator-permissions/tslint.json b/types/navigator-permissions/tslint.json new file mode 100644 index 0000000000..3db14f85ea --- /dev/null +++ b/types/navigator-permissions/tslint.json @@ -0,0 +1 @@ +{ "extends": "dtslint/dt.json" }