diff --git a/src/cli/extract.ts b/src/cli/extract.ts index 7a1f730..a5f537a 100644 --- a/src/cli/extract.ts +++ b/src/cli/extract.ts @@ -23,7 +23,7 @@ const FORMAT_METHOD_NAMES = new Set(['format', '_', 't']); function isFormatCall(node: Node, imports: Set) { if (node.type !== 'CallExpression') return false; - let identifier: Identifier; + let identifier: Identifier | undefined; if (node.callee.type === 'Identifier') { identifier = node.callee; @@ -125,7 +125,9 @@ export function collectMessageDefinitions(ast: Ast) { definitionDict.properties.map((propNode) => { if (propNode.type !== 'Property') { throw new Error( - `Found invalid '${propNode.type}' at L${propNode.loc.start.line}:${propNode.loc.start.column}`, + `Found invalid '${propNode.type}' at L${propNode.loc!.start.line}:${ + propNode.loc!.start.column + }`, ); } @@ -143,7 +145,7 @@ export function collectMessages(markup: string): Message[] { ...definitions.map((definition) => getObjFromExpression(definition)), ...calls.map((call) => { const [pathNode, options] = call.arguments; - let messageObj; + let messageObj: Partial; if (pathNode.type === 'ObjectExpression') { // _({ ...opts }) @@ -166,7 +168,7 @@ export function collectMessages(markup: string): Message[] { return messageObj; }), - ].filter(Boolean); + ].filter((Boolean as unknown) as (x: Message | null) => x is Message); } export function extractMessages( diff --git a/src/cli/includes/getObjFromExpression.ts b/src/cli/includes/getObjFromExpression.ts index 81938c4..cd65b75 100644 --- a/src/cli/includes/getObjFromExpression.ts +++ b/src/cli/includes/getObjFromExpression.ts @@ -2,8 +2,10 @@ import type { ObjectExpression, Property, Identifier } from 'estree'; import type { Message } from '../types'; -export function getObjFromExpression(exprNode: ObjectExpression) { - return exprNode.properties.reduce((acc, prop: Property) => { +export function getObjFromExpression( + exprNode: ObjectExpression, +): Partial { + return exprNode.properties.reduce>((acc, prop: Property) => { // we only want primitives if ( prop.value.type === 'Literal' && diff --git a/src/cli/types/index.ts b/src/cli/types/index.ts index 4f1c93b..cdd0c04 100644 --- a/src/cli/types/index.ts +++ b/src/cli/types/index.ts @@ -1,5 +1,5 @@ export interface Message { - id?: string; - default?: string; + id: string; + default: string; [key: string]: any; } diff --git a/src/runtime/configs.ts b/src/runtime/configs.ts index 53fdaa4..204ca17 100644 --- a/src/runtime/configs.ts +++ b/src/runtime/configs.ts @@ -1,4 +1,4 @@ -import type { ConfigureOptions } from './types'; +import type { ConfigureOptions, ConfigureOptionsInit } from './types'; import { $locale } from './stores/locale'; interface Formats { @@ -47,13 +47,13 @@ export const defaultOptions: ConfigureOptions = { ignoreTag: true, }; -const options: ConfigureOptions = defaultOptions; +const options: ConfigureOptions = defaultOptions as any; export function getOptions() { return options; } -export function init(opts: ConfigureOptions) { +export function init(opts: ConfigureOptionsInit) { const { formats, ...rest } = opts; const initialLocale = opts.initialLocale || opts.fallbackLocale; diff --git a/src/runtime/includes/formatters.ts b/src/runtime/includes/formatters.ts index caedceb..46dd2ad 100644 --- a/src/runtime/includes/formatters.ts +++ b/src/runtime/includes/formatters.ts @@ -1,6 +1,9 @@ import IntlMessageFormat from 'intl-messageformat'; -import type { MemoizedIntlFormatter } from '../types'; +import type { + MemoizedIntlFormatter, + MemoizedIntlFormatterOptional, +} from '../types'; import { getCurrentLocale } from '../stores/locale'; import { getOptions } from '../configs'; import { monadicMemoize } from './memoize'; @@ -15,6 +18,16 @@ type MemoizedDateTimeFormatterFactory = MemoizedIntlFormatter< Intl.DateTimeFormatOptions >; +type MemoizedNumberFormatterFactoryOptional = MemoizedIntlFormatterOptional< + Intl.NumberFormat, + Intl.NumberFormatOptions +>; + +type MemoizedDateTimeFormatterFactoryOptional = MemoizedIntlFormatterOptional< + Intl.DateTimeFormat, + Intl.DateTimeFormatOptions +>; + const getIntlFormatterOptions = ( type: 'time' | 'number' | 'date', name: string, @@ -76,17 +89,17 @@ const createTimeFormatter: MemoizedDateTimeFormatterFactory = monadicMemoize( }, ); -export const getNumberFormatter: MemoizedNumberFormatterFactory = ({ +export const getNumberFormatter: MemoizedNumberFormatterFactoryOptional = ({ locale = getCurrentLocale(), ...args } = {}) => createNumberFormatter({ locale, ...args }); -export const getDateFormatter: MemoizedDateTimeFormatterFactory = ({ +export const getDateFormatter: MemoizedDateTimeFormatterFactoryOptional = ({ locale = getCurrentLocale(), ...args } = {}) => createDateFormatter({ locale, ...args }); -export const getTimeFormatter: MemoizedDateTimeFormatterFactory = ({ +export const getTimeFormatter: MemoizedDateTimeFormatterFactoryOptional = ({ locale = getCurrentLocale(), ...args } = {}) => createTimeFormatter({ locale, ...args }); diff --git a/src/runtime/includes/loaderQueue.ts b/src/runtime/includes/loaderQueue.ts index 6b01161..007d892 100644 --- a/src/runtime/includes/loaderQueue.ts +++ b/src/runtime/includes/loaderQueue.ts @@ -62,7 +62,7 @@ function loadLocaleQueue(locale: string, localeQueue: MessagesLoader[]) { const activeFlushes: { [key: string]: Promise } = {}; -export function flush(locale: string): Promise { +export async function flush(locale: string): Promise { if (!hasLocaleQueue(locale)) { if (locale in activeFlushes) { return activeFlushes[locale]; diff --git a/src/runtime/includes/lookup.ts b/src/runtime/includes/lookup.ts index 921fe7c..dcfad8c 100644 --- a/src/runtime/includes/lookup.ts +++ b/src/runtime/includes/lookup.ts @@ -15,7 +15,7 @@ const addToCache = (path: string, locale: string, message: string) => { return message; }; -export const lookup = (path: string, refLocale: string) => { +export const lookup = (path: string, refLocale: string | null | undefined) => { if (refLocale == null) return undefined; if (refLocale in lookupCache && path in lookupCache[refLocale]) { diff --git a/src/runtime/index.ts b/src/runtime/index.ts index ad58480..842022e 100644 --- a/src/runtime/index.ts +++ b/src/runtime/index.ts @@ -31,7 +31,7 @@ export function defineMessages(i: Record) { } export function waitLocale(locale?: string) { - return flush(locale || getCurrentLocale() || getOptions().initialLocale); + return flush(locale || getCurrentLocale() || getOptions().initialLocale!); } export { diff --git a/src/runtime/stores/dictionary.ts b/src/runtime/stores/dictionary.ts index bce55ea..701c385 100644 --- a/src/runtime/stores/dictionary.ts +++ b/src/runtime/stores/dictionary.ts @@ -34,7 +34,9 @@ export function getMessageFromDictionary(locale: string, id: string) { return match; } -export function getClosestAvailableLocale(refLocale: string): string | null { +export function getClosestAvailableLocale( + refLocale: string, +): string | null | undefined { if (refLocale == null) return undefined; const relatedLocales = getPossibleLocales(refLocale); diff --git a/src/runtime/stores/formatters.ts b/src/runtime/stores/formatters.ts index 3a0eb1b..12af044 100644 --- a/src/runtime/stores/formatters.ts +++ b/src/runtime/stores/formatters.ts @@ -23,7 +23,7 @@ import { getCurrentLocale, getPossibleLocales, $locale } from './locale'; const formatMessage: MessageFormatter = (id, options = {}) => { if (typeof id === 'object') { options = id as MessageObject; - id = options.id; + id = options.id!; } const { @@ -90,11 +90,8 @@ const formatNumber: NumberFormatter = (n, options) => { return getNumberFormatter(options).format(n); }; -const getJSON: JSONGetter = ( - id: string, - locale = getCurrentLocale(), -) => { - return lookup(id, locale) as T; +const getJSON: JSONGetter = (id: string, locale = getCurrentLocale()): any => { + return lookup(id, locale); }; export const $format = derived([$locale, $dictionary], () => formatMessage); diff --git a/src/runtime/stores/locale.ts b/src/runtime/stores/locale.ts index 4ada4dd..04cfc96 100644 --- a/src/runtime/stores/locale.ts +++ b/src/runtime/stores/locale.ts @@ -6,7 +6,7 @@ import { getClosestAvailableLocale } from './dictionary'; import { $isLoading } from './loading'; let current: string; -const $locale = writable(null); +const $locale = writable(null); function getSubLocales(refLocale: string) { return refLocale @@ -77,7 +77,8 @@ $locale.set = (newLocale: string): void | Promise => { }; // istanbul ignore next -$locale.update = (fn: (locale: string) => void | Promise) => - localeSet(fn(current)); +$locale.update = ( + fn: (value: string | null | undefined) => string | null | undefined, +) => localeSet(fn(current)); export { $locale }; diff --git a/src/runtime/types/index.ts b/src/runtime/types/index.ts index 76a6b05..a2b5e1b 100644 --- a/src/runtime/types/index.ts +++ b/src/runtime/types/index.ts @@ -1,7 +1,11 @@ import type { FormatXMLElementFn, Formats } from 'intl-messageformat'; export interface LocaleDictionary { - [key: string]: LocaleDictionary | string | Array; + [key: string]: + | LocaleDictionary + | string + | Array + | null; } export type LocalesDictionary = { @@ -49,7 +53,7 @@ export type NumberFormatter = ( options?: IntlFormatterOptions, ) => string; -export type JSONGetter = (id: string, locale?: string) => T; +export type JSONGetter = (id: string, locale?: string) => any; type IntlFormatterOptions = T & { format?: string; @@ -57,6 +61,10 @@ type IntlFormatterOptions = T & { }; export interface MemoizedIntlFormatter { + (options: IntlFormatterOptions): T; +} + +export interface MemoizedIntlFormatterOptional { (options?: IntlFormatterOptions): T; } @@ -65,10 +73,14 @@ export interface MessagesLoader { } export interface ConfigureOptions { - fallbackLocale: string; - formats?: Partial; - initialLocale?: string; - loadingDelay?: number; - warnOnMissingMessages?: boolean; - ignoreTag?: boolean; + fallbackLocale: string | null | undefined; + formats: Formats; + initialLocale: string | null; + loadingDelay: number; + warnOnMissingMessages: boolean; + ignoreTag: boolean; } + +export type ConfigureOptionsInit = Pick & + Partial>> & + Partial>; diff --git a/test/runtime/includes/loaderQueue.test.ts b/test/runtime/includes/loaderQueue.test.ts index ebc9bcc..0b0f03f 100644 --- a/test/runtime/includes/loaderQueue.test.ts +++ b/test/runtime/includes/loaderQueue.test.ts @@ -23,8 +23,8 @@ test('checks if exist queues of locale and its fallbacks', () => { expect(hasLocaleQueue('en-US')).toBe(true); }); -test("does nothing if there's no queue for a locale", () => { - expect(flush('foo')).toBeUndefined(); +test("does nothing if there's no queue for a locale", async () => { + expect(await flush('foo')).toBeUndefined(); }); test('flushes the queue of a locale and its fallbacks and merge the result with the dictionary', async () => { diff --git a/test/runtime/includes/utils.test.ts b/test/runtime/includes/utils.test.ts index c2fac9a..46d4647 100644 --- a/test/runtime/includes/utils.test.ts +++ b/test/runtime/includes/utils.test.ts @@ -8,7 +8,7 @@ import { describe('getting client locale', () => { beforeEach(() => { - delete window.location; + delete (window as any).location; window.location = { pathname: '/', hostname: 'example.com', diff --git a/tsconfig.json b/tsconfig.json index 2c88793..d85e826 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,12 @@ { "compilerOptions": { "allowJs": true, + "strictNullChecks": true, "noImplicitAny": true, "sourceMap": false, "module": "esnext", "moduleResolution": "node", + "skipLibCheck": true, "esModuleInterop": true, "resolveJsonModule": true, "target": "es2017", diff --git a/yarn.lock b/yarn.lock index d4917ff..fbdbffc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1263,9 +1263,9 @@ integrity sha512-lg55ArB+ZiHHbBBttLpzD07akz0QPrZgUODNakeC09i62dnrywr9mFErHuaPlB6I7z+sEbK+IYmplahvplCj2g== "@types/node@^14.14.35": - version "14.14.35" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.35.tgz#42c953a4e2b18ab931f72477e7012172f4ffa313" - integrity sha512-Lt+wj8NVPx0zUmUwumiVXapmaLUcAk3yPuHCFVXras9k5VT9TdhJqKqGVUQCD60OTMCl0qxJ57OiTL0Mic3Iag== + version "14.17.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.11.tgz#82d266d657aec5ff01ca59f2ffaff1bb43f7bf0f" + integrity sha512-n2OQ+0Bz6WEsUjrvcHD1xZ8K+Kgo4cn9/w94s1bJS690QMUWfJPW/m7CCb7gPkA1fcYwL2UpjXP/rq/Eo41m6w== "@types/normalize-package-data@^2.4.0": version "2.4.0"