diff --git a/package.json b/package.json index dedec73edb..1b9e01033e 100644 --- a/package.json +++ b/package.json @@ -212,7 +212,7 @@ "stylelint": "^14.9.1", "stylelint-config-standard": "^26.0.0", "stylelint-scss": "^4.2.0", - "typescript": "4.7.4", + "typescript": "4.8.4", "walk": "^2.3.14" }, "jest": { diff --git a/src/@types/common.ts b/src/@types/common.ts index b18fefc253..e054cc1da8 100644 --- a/src/@types/common.ts +++ b/src/@types/common.ts @@ -24,15 +24,16 @@ export type Writeable = { -readonly [P in keyof T]: T[P] }; export type ComponentClass = keyof JSX.IntrinsicElements | JSXElementConstructor; export type ReactAnyComponent = React.Component | React.ExoticComponent; +// Utility type for string dot notation for accessing nested object properties // Based on https://stackoverflow.com/a/58436959 type Join = K extends string | number ? P extends string | number ? `${K}${"" extends P ? "" : "."}${P}` : never : never; -type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...0[]]; +type Prev = [never, 0, 1, 2, 3, ...0[]]; -export type Leaves = [D] extends [never] ? never : T extends object ? +export type Leaves = [D] extends [never] ? never : T extends object ? { [K in keyof T]-?: Join> }[keyof T] : ""; export type RecursivePartial = { diff --git a/src/@types/commonmark.ts b/src/@types/commonmark.ts new file mode 100644 index 0000000000..1ea13c7eba --- /dev/null +++ b/src/@types/commonmark.ts @@ -0,0 +1,52 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import * as commonmark from "commonmark"; + +declare module "commonmark" { + export type Attr = [key: string, value: string]; + + export interface HtmlRenderer { + // As far as @types/commonmark is concerned, these are not public, so add them + // https://github.com/commonmark/commonmark.js/blob/master/lib/render/html.js#L272-L296 + text: (this: commonmark.HtmlRenderer, node: commonmark.Node) => void; + html_inline: (this: commonmark.HtmlRenderer, node: commonmark.Node) => void; + html_block: (this: commonmark.HtmlRenderer, node: commonmark.Node) => void; + // softbreak: () => void; // This one can't be correctly specified as it is wrongly defined in @types/commonmark + linebreak: (this: commonmark.HtmlRenderer) => void; + link: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void; + image: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void; + emph: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void; + strong: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void; + paragraph: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void; + heading: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void; + code: (this: commonmark.HtmlRenderer, node: commonmark.Node) => void; + code_block: (this: commonmark.HtmlRenderer, node: commonmark.Node) => void; + thematic_break: (this: commonmark.HtmlRenderer, node: commonmark.Node) => void; + block_quote: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void; + list: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void; + item: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void; + custom_inline: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void; + custom_block: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void; + esc: (s: string) => string; + out: (this: commonmark.HtmlRenderer, text: string) => void; + tag: (this: commonmark.HtmlRenderer, name: string, attrs?: Attr[], selfClosing?: boolean) => void; + attrs: (this: commonmark.HtmlRenderer, node: commonmark.Node) => Attr[]; + // These are inherited from the base Renderer + lit: (this: commonmark.HtmlRenderer, text: string) => void; + cr: (this: commonmark.HtmlRenderer) => void; + } +} diff --git a/src/@types/diff-dom.ts b/src/@types/diff-dom.d.ts similarity index 100% rename from src/@types/diff-dom.ts rename to src/@types/diff-dom.d.ts diff --git a/src/@types/png-chunks-extract.ts b/src/@types/png-chunks-extract.d.ts similarity index 100% rename from src/@types/png-chunks-extract.ts rename to src/@types/png-chunks-extract.d.ts diff --git a/src/@types/sanitize-html.ts b/src/@types/sanitize-html.d.ts similarity index 100% rename from src/@types/sanitize-html.ts rename to src/@types/sanitize-html.d.ts diff --git a/src/Markdown.ts b/src/Markdown.ts index eb36942e95..182066ba82 100644 --- a/src/Markdown.ts +++ b/src/Markdown.ts @@ -15,6 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import "./@types/commonmark"; // import better types than @types/commonmark import * as commonmark from 'commonmark'; import { escape } from "lodash"; import { logger } from 'matrix-js-sdk/src/logger'; @@ -26,17 +27,6 @@ const ALLOWED_HTML_TAGS = ['sub', 'sup', 'del', 'u']; // These types of node are definitely text const TEXT_NODES = ['text', 'softbreak', 'linebreak', 'paragraph', 'document']; -// As far as @types/commonmark is concerned, these are not public, so add them -interface CommonmarkHtmlRendererInternal extends commonmark.HtmlRenderer { - paragraph: (node: commonmark.Node, entering: boolean) => void; - link: (node: commonmark.Node, entering: boolean) => void; - html_inline: (node: commonmark.Node) => void; // eslint-disable-line camelcase - html_block: (node: commonmark.Node) => void; // eslint-disable-line camelcase - text: (node: commonmark.Node) => void; - out: (text: string) => void; - emph: (node: commonmark.Node) => void; -} - function isAllowedHtmlTag(node: commonmark.Node): boolean { if (node.literal != null && node.literal.match('^<((div|span) data-mx-maths="[^"]*"|/(div|span))>$') != null) { @@ -248,7 +238,7 @@ export default class Markdown { isPlainText(): boolean { const walker = this.parsed.walker(); - let ev; + let ev: commonmark.NodeWalkingStep; while (ev = walker.next()) { const node = ev.node; if (TEXT_NODES.indexOf(node.type) > -1) { @@ -278,7 +268,7 @@ export default class Markdown { // block quote ends up all on one line // (https://github.com/vector-im/element-web/issues/3154) softbreak: '
', - }) as CommonmarkHtmlRendererInternal; + }); // Trying to strip out the wrapping

causes a lot more complication // than it's worth, i think. For instance, this code will go and strip @@ -356,7 +346,7 @@ export default class Markdown { * which has no formatting. Otherwise it emits HTML(!). */ toPlaintext(): string { - const renderer = new commonmark.HtmlRenderer({ safe: false }) as CommonmarkHtmlRendererInternal; + const renderer = new commonmark.HtmlRenderer({ safe: false }); renderer.paragraph = function(node: commonmark.Node, entering: boolean) { // as with toHTML, only append lines to paragraphs if there are diff --git a/src/Notifier.ts b/src/Notifier.ts index f675775b9e..bc7bae71c4 100644 --- a/src/Notifier.ts +++ b/src/Notifier.ts @@ -210,7 +210,7 @@ export const Notifier = { } }, - start: function() { + start: function(this: typeof Notifier) { // do not re-bind in the case of repeated call this.boundOnEvent = this.boundOnEvent || this.onEvent.bind(this); this.boundOnSyncStateChange = this.boundOnSyncStateChange || this.onSyncStateChange.bind(this); @@ -225,7 +225,7 @@ export const Notifier = { this.isSyncing = false; }, - stop: function() { + stop: function(this: typeof Notifier) { if (MatrixClientPeg.get()) { MatrixClientPeg.get().removeListener(ClientEvent.Event, this.boundOnEvent); MatrixClientPeg.get().removeListener(RoomEvent.Receipt, this.boundOnRoomReceipt); @@ -322,7 +322,7 @@ export const Notifier = { return SettingsStore.getValue("audioNotificationsEnabled"); }, - setPromptHidden: function(hidden: boolean, persistent = true) { + setPromptHidden: function(this: typeof Notifier, hidden: boolean, persistent = true) { this.toolbarHidden = hidden; hideNotificationsToast(); @@ -343,7 +343,7 @@ export const Notifier = { !this.isEnabled() && !this._isPromptHidden(); }, - _isPromptHidden: function() { + _isPromptHidden: function(this: typeof Notifier) { // Check localStorage for any such meta data if (global.localStorage) { return global.localStorage.getItem("notifications_hidden") === "true"; @@ -352,7 +352,7 @@ export const Notifier = { return this.toolbarHidden; }, - onSyncStateChange: function(state: SyncState, prevState?: SyncState, data?: ISyncStateData) { + onSyncStateChange: function(this: typeof Notifier, state: SyncState, prevState?: SyncState, data?: ISyncStateData) { if (state === SyncState.Syncing) { this.isSyncing = true; } else if (state === SyncState.Stopped || state === SyncState.Error) { @@ -368,7 +368,7 @@ export const Notifier = { } }, - onEvent: function(ev: MatrixEvent) { + onEvent: function(this: typeof Notifier, ev: MatrixEvent) { if (!this.isSyncing) return; // don't alert for any messages initially if (ev.getSender() === MatrixClientPeg.get().getUserId()) return; diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 767a42e38a..cf6375807f 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -111,7 +111,7 @@ export const CommandCategories = { export type RunResult = XOR<{ error: Error | ITranslatableError }, { promise: Promise }>; -type RunFn = ((roomId: string, args: string, cmd: string) => RunResult); +type RunFn = ((this: Command, roomId: string, args: string) => RunResult); interface ICommandOpts { command: string; @@ -129,9 +129,9 @@ interface ICommandOpts { export class Command { public readonly command: string; public readonly aliases: string[]; - public readonly args: undefined | string; + public readonly args?: string; public readonly description: string; - public readonly runFn: undefined | RunFn; + public readonly runFn?: RunFn; public readonly category: string; public readonly hideCompletionAfterSpace: boolean; public readonly renderingTypes?: TimelineRenderingType[]; @@ -143,7 +143,7 @@ export class Command { this.aliases = opts.aliases || []; this.args = opts.args || ""; this.description = opts.description; - this.runFn = opts.runFn; + this.runFn = opts.runFn?.bind(this); this.category = opts.category || CommandCategories.other; this.hideCompletionAfterSpace = opts.hideCompletionAfterSpace || false; this._isEnabled = opts.isEnabled; @@ -188,7 +188,7 @@ export class Command { }); } - return this.runFn.bind(this)(roomId, args); + return this.runFn(roomId, args); } public getUsage() { @@ -1114,7 +1114,7 @@ export const Commands = [ description: _td("Sends the given message coloured as a rainbow"), args: '', runFn: function(roomId, args) { - if (!args) return reject(this.getUserId()); + if (!args) return reject(this.getUsage()); return successSync(ContentHelpers.makeHtmlMessage(args, textToHtmlRainbow(args))); }, category: CommandCategories.messages, @@ -1124,7 +1124,7 @@ export const Commands = [ description: _td("Sends the given emote coloured as a rainbow"), args: '', runFn: function(roomId, args) { - if (!args) return reject(this.getUserId()); + if (!args) return reject(this.getUsage()); return successSync(ContentHelpers.makeHtmlEmote(args, textToHtmlRainbow(args))); }, category: CommandCategories.messages, @@ -1207,7 +1207,7 @@ export const Commands = [ return success((async () => { if (isPhoneNumber) { - const results = await LegacyCallHandler.instance.pstnLookup(this.state.value); + const results = await LegacyCallHandler.instance.pstnLookup(userId); if (!results || results.length === 0 || !results[0].userid) { throw newTranslatableError("Unable to find Matrix ID for phone number"); } diff --git a/src/autocomplete/QueryMatcher.ts b/src/autocomplete/QueryMatcher.ts index e8746c12d8..398cdfe980 100644 --- a/src/autocomplete/QueryMatcher.ts +++ b/src/autocomplete/QueryMatcher.ts @@ -45,7 +45,7 @@ interface IOptions { * @param {function[]} options.funcs List of functions that when called with the * object as an arg will return a string to use as an index */ -export default class QueryMatcher { +export default class QueryMatcher { private _options: IOptions; private _items: Map; diff --git a/src/components/views/elements/Draggable.tsx b/src/components/views/elements/Draggable.tsx index a6eb8323f3..659af349de 100644 --- a/src/components/views/elements/Draggable.tsx +++ b/src/components/views/elements/Draggable.tsx @@ -47,7 +47,7 @@ export default class Draggable extends React.Component { }; } - private onMouseDown = (event: MouseEvent): void => { + private onMouseDown = (event: React.MouseEvent): void => { this.setState({ location: { currentX: event.clientX, @@ -74,6 +74,6 @@ export default class Draggable extends React.Component { } render() { - return

; + return
; } } diff --git a/src/components/views/elements/IRCTimelineProfileResizer.tsx b/src/components/views/elements/IRCTimelineProfileResizer.tsx index 61fee35bdd..01a55f0202 100644 --- a/src/components/views/elements/IRCTimelineProfileResizer.tsx +++ b/src/components/views/elements/IRCTimelineProfileResizer.tsx @@ -48,7 +48,7 @@ export default class IRCTimelineProfileResizer extends React.Component this.updateCSSWidth(this.state.width)); } - private dragFunc = (location: ILocationState, event: React.MouseEvent): ILocationState => { + private dragFunc = (location: ILocationState, event: MouseEvent): ILocationState => { const offset = event.clientX - location.currentX; const newWidth = this.state.width + offset; @@ -77,7 +77,7 @@ export default class IRCTimelineProfileResizer extends React.Component { if (this.props.roomId) { SettingsStore.setValue( "ircDisplayNameWidth", @@ -86,13 +86,13 @@ export default class IRCTimelineProfileResizer extends React.Component; } } diff --git a/src/components/views/elements/Validation.tsx b/src/components/views/elements/Validation.tsx index 66c197abc8..50c3711a2d 100644 --- a/src/components/views/elements/Validation.tsx +++ b/src/components/views/elements/Validation.tsx @@ -15,7 +15,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -/* eslint-disable @typescript-eslint/no-invalid-this */ import React from "react"; import classNames from "classnames"; @@ -46,7 +45,7 @@ interface IArgs { export interface IFieldState { value: string; focused: boolean; - allowEmpty: boolean; + allowEmpty?: boolean; } export interface IValidationResult { @@ -80,10 +79,13 @@ export interface IValidationResult { * A validation function that takes in the current input value and returns * the overall validity and a feedback UI that can be rendered for more detail. */ -export default function withValidation({ +export default function withValidation({ description, hideDescriptionIfValid, deriveData, rules, }: IArgs) { - return async function onValidate({ value, focused, allowEmpty = true }: IFieldState): Promise { + return async function onValidate( + this: T, + { value, focused, allowEmpty = true }: IFieldState, + ): Promise { if (!value && allowEmpty) { return { valid: null, @@ -96,7 +98,7 @@ export default function withValidation({ const results: IResult[] = []; let valid = true; - if (rules && rules.length) { + if (rules?.length) { for (const rule of rules) { if (!rule.key || !rule.test) { continue; diff --git a/src/utils/Image.ts b/src/utils/Image.ts index d4f5ba69eb..a78d6380e2 100644 --- a/src/utils/Image.ts +++ b/src/utils/Image.ts @@ -31,7 +31,7 @@ function arrayBufferReadInt(arr: ArrayBuffer, start: number): number { } function arrayBufferReadStr(arr: ArrayBuffer, start: number, len: number): string { - return String.fromCharCode.apply(null, arrayBufferRead(arr, start, len)); + return String.fromCharCode.apply(null, Array.from(arrayBufferRead(arr, start, len))); } export async function blobIsAnimated(mimeType: string | undefined, blob: Blob): Promise { diff --git a/src/utils/MegolmExportEncryption.ts b/src/utils/MegolmExportEncryption.ts index 8d7f0a00ad..2545f7f391 100644 --- a/src/utils/MegolmExportEncryption.ts +++ b/src/utils/MegolmExportEncryption.ts @@ -356,7 +356,7 @@ function packMegolmKeyFile(data: Uint8Array): ArrayBuffer { function encodeBase64(uint8Array: Uint8Array): string { // Misinterpt the Uint8Array as Latin-1. // window.btoa expects a unicode string with codepoints in the range 0-255. - const latin1String = String.fromCharCode.apply(null, uint8Array); + const latin1String = String.fromCharCode.apply(null, Array.from(uint8Array)); // Use the builtin base64 encoder. return window.btoa(latin1String); } diff --git a/test/ContentMessages-test.ts b/test/ContentMessages-test.ts index 966b5d5159..3d812cbbee 100644 --- a/test/ContentMessages-test.ts +++ b/test/ContentMessages-test.ts @@ -131,9 +131,9 @@ describe("ContentMessages", () => { jest.spyOn(document, "createElement").mockImplementation(tagName => { const element = createElement(tagName); if (tagName === "video") { - element.load = jest.fn(); - element.play = () => element.onloadeddata(new Event("loadeddata")); - element.pause = jest.fn(); + (element).load = jest.fn(); + (element).play = () => element.onloadeddata(new Event("loadeddata")); + (element).pause = jest.fn(); Object.defineProperty(element, 'videoHeight', { get() { return 600; }, }); diff --git a/test/Notifier-test.ts b/test/Notifier-test.ts index 3dbeae4901..f15e798426 100644 --- a/test/Notifier-test.ts +++ b/test/Notifier-test.ts @@ -433,4 +433,11 @@ describe("Notifier", () => { expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(1); }); }); + + describe("setPromptHidden", () => { + it("should persist by default", () => { + Notifier.setPromptHidden(true); + expect(localStorage.getItem("notifications_hidden")).toBeTruthy(); + }); + }); }); diff --git a/test/SlashCommands-test.tsx b/test/SlashCommands-test.tsx index 287c284786..896841ad35 100644 --- a/test/SlashCommands-test.tsx +++ b/test/SlashCommands-test.tsx @@ -225,4 +225,19 @@ describe('SlashCommands', () => { expect(client.leaveRoomChain).toHaveBeenCalledWith("room-id", expect.anything()); }); }); + + describe.each([ + "rainbow", + "rainbowme", + ])("/%s", (commandName: string) => { + const command = findCommand(commandName); + + it("should return usage if no args", () => { + expect(command.run(roomId, null, null).error).toBe(command.getUsage()); + }); + + it("should make things rainbowy", () => { + return expect(command.run(roomId, null, "this is a test message").promise).resolves.toMatchSnapshot(); + }); + }); }); diff --git a/test/__snapshots__/SlashCommands-test.tsx.snap b/test/__snapshots__/SlashCommands-test.tsx.snap new file mode 100644 index 0000000000..925f5e878b --- /dev/null +++ b/test/__snapshots__/SlashCommands-test.tsx.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SlashCommands /rainbow should make things rainbowy 1`] = ` +{ + "body": "this is a test message", + "format": "org.matrix.custom.html", + "formatted_body": "this is a test message", + "msgtype": "m.text", +} +`; + +exports[`SlashCommands /rainbowme should make things rainbowy 1`] = ` +{ + "body": "this is a test message", + "format": "org.matrix.custom.html", + "formatted_body": "this is a test message", + "msgtype": "m.emote", +} +`; diff --git a/test/components/views/Validation-test.ts b/test/components/views/Validation-test.ts new file mode 100644 index 0000000000..b76d4ecb78 --- /dev/null +++ b/test/components/views/Validation-test.ts @@ -0,0 +1,31 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import withValidation from "../../../src/components/views/elements/Validation"; + +describe("Validation", () => { + it("should handle 0 rules", () => { + const handler = withValidation({ + rules: [], + }); + return expect(handler({ + value: "value", + focused: true, + })).resolves.toEqual(expect.objectContaining({ + valid: true, + })); + }); +}); diff --git a/test/test-utils/audio.ts b/test/test-utils/audio.ts index 212ef51362..a49a275e4c 100644 --- a/test/test-utils/audio.ts +++ b/test/test-utils/audio.ts @@ -46,20 +46,20 @@ export const createTestPlayback = (): Playback => { return true; }, // EventEmitter - on: eventEmitter.on.bind(eventEmitter), - once: eventEmitter.once.bind(eventEmitter), - off: eventEmitter.off.bind(eventEmitter), - addListener: eventEmitter.addListener.bind(eventEmitter), - removeListener: eventEmitter.removeListener.bind(eventEmitter), - removeAllListeners: eventEmitter.removeAllListeners.bind(eventEmitter), - getMaxListeners: eventEmitter.getMaxListeners.bind(eventEmitter), - setMaxListeners: eventEmitter.setMaxListeners.bind(eventEmitter), - listeners: eventEmitter.listeners.bind(eventEmitter), - rawListeners: eventEmitter.rawListeners.bind(eventEmitter), - listenerCount: eventEmitter.listenerCount.bind(eventEmitter), - eventNames: eventEmitter.eventNames.bind(eventEmitter), - prependListener: eventEmitter.prependListener.bind(eventEmitter), - prependOnceListener: eventEmitter.prependOnceListener.bind(eventEmitter), + on: eventEmitter.on.bind(eventEmitter) as Playback["on"], + once: eventEmitter.once.bind(eventEmitter) as Playback["once"], + off: eventEmitter.off.bind(eventEmitter) as Playback["off"], + addListener: eventEmitter.addListener.bind(eventEmitter) as Playback["addListener"], + removeListener: eventEmitter.removeListener.bind(eventEmitter) as Playback["removeListener"], + removeAllListeners: eventEmitter.removeAllListeners.bind(eventEmitter) as Playback["removeAllListeners"], + getMaxListeners: eventEmitter.getMaxListeners.bind(eventEmitter) as Playback["getMaxListeners"], + setMaxListeners: eventEmitter.setMaxListeners.bind(eventEmitter) as Playback["setMaxListeners"], + listeners: eventEmitter.listeners.bind(eventEmitter) as Playback["listeners"], + rawListeners: eventEmitter.rawListeners.bind(eventEmitter) as Playback["rawListeners"], + listenerCount: eventEmitter.listenerCount.bind(eventEmitter) as Playback["listenerCount"], + eventNames: eventEmitter.eventNames.bind(eventEmitter) as Playback["eventNames"], + prependListener: eventEmitter.prependListener.bind(eventEmitter) as Playback["prependListener"], + prependOnceListener: eventEmitter.prependOnceListener.bind(eventEmitter) as Playback["prependOnceListener"], liveData: new SimpleObservable(), durationSeconds: 31415, timeSeconds: 3141, diff --git a/tsconfig.json b/tsconfig.json index 69749ab96b..46ac495c86 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,7 +17,10 @@ "es2020", "dom", "dom.iterable" - ] + ], + "alwaysStrict": true, + "strictBindCallApply": true, + "noImplicitThis": true }, "include": [ "./src/**/*.ts", diff --git a/yarn.lock b/yarn.lock index cdaa4ec373..eb40ee0d9f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9451,10 +9451,10 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@4.7.4: - version "4.7.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" - integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== +typescript@4.8.4: + version "4.8.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" + integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== ua-parser-js@^0.7.30: version "0.7.31"