diff --git a/packages/js/currency/changelog/dev-33101-migrate-woo-currency-to-ts b/packages/js/currency/changelog/dev-33101-migrate-woo-currency-to-ts new file mode 100644 index 00000000000..889efb834ca --- /dev/null +++ b/packages/js/currency/changelog/dev-33101-migrate-woo-currency-to-ts @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Migrate @woocommerce/currency to TS diff --git a/packages/js/currency/package.json b/packages/js/currency/package.json index aa2d964334d..e1f46ec2643 100644 --- a/packages/js/currency/package.json +++ b/packages/js/currency/package.json @@ -19,6 +19,7 @@ }, "main": "build/index.js", "module": "build-module/index.js", + "types": "build-types", "react-native": "src/index", "dependencies": { "@woocommerce/number": "workspace:*", diff --git a/packages/js/currency/src/index.js b/packages/js/currency/src/index.tsx similarity index 79% rename from packages/js/currency/src/index.js rename to packages/js/currency/src/index.tsx index c948f6b28c4..88eca6d7555 100644 --- a/packages/js/currency/src/index.js +++ b/packages/js/currency/src/index.tsx @@ -4,7 +4,7 @@ import { createElement } from '@wordpress/element'; import { decodeEntities } from '@wordpress/html-entities'; import { sprintf } from '@wordpress/i18n'; -import { numberFormat } from '@woocommerce/number'; +import { NumberConfig, numberFormat } from '@woocommerce/number'; import deprecated from '@wordpress/deprecated'; /** @@ -18,26 +18,106 @@ import deprecated from '@wordpress/deprecated'; * @typedef {NumberConfig & CurrencyProps} CurrencyConfig */ +export type SymbolPosition = 'left' | 'right' | 'left_space' | 'right_space'; + +export type CurrencyProps = { + code: string; + symbol: string; + symbolPosition: SymbolPosition; + priceFormat?: string; +}; + +export type CurrencyConfig = Partial< NumberConfig & CurrencyProps >; + +export type Currency = { + code: string; + symbol: string; + symbolPosition: string; + decimalSeparator: string; + priceFormat: string; + thousandSeparator: string; + precision: number; +}; + +export type CountryInfo = { + // https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/i18n/locale-info.php#L15-L28 + currency_code: string; + currency_pos: SymbolPosition; + thousand_sep: string; + decimal_sep: string; + num_decimals: number; + weight_unit: string; + dimension_unit: string; + direction: string; + default_locale: string; + name: string; + singular: string; + plural: string; + short_symbol: string; + locales: string[]; +}; + /** * * @param {CurrencyConfig} currencySetting * @return {Object} currency object */ -const CurrencyFactory = function ( currencySetting ) { - let currency; +const CurrencyFactory = function ( currencySetting?: CurrencyConfig ) { + let currency: Currency; - setCurrency( currencySetting ); + function stripTags( str: string ) { + const tmp = document.createElement( 'DIV' ); + tmp.innerHTML = str; + return tmp.textContent || tmp.innerText || ''; + } - function setCurrency( setting ) { + /** + * Get the default price format from a currency. + * + * @param {CurrencyConfig} config Currency configuration. + * @return {string} Price format. + */ + function getPriceFormat( config: CurrencyConfig ) { + if ( config.priceFormat ) { + return stripTags( config.priceFormat.toString() ); + } + + switch ( config.symbolPosition ) { + case 'left': + return '%1$s%2$s'; + case 'right': + return '%2$s%1$s'; + case 'left_space': + return '%1$s %2$s'; + case 'right_space': + return '%2$s %1$s'; + } + + return '%1$s%2$s'; + } + + function setCurrency( setting?: CurrencyConfig ) { const defaultCurrency = { code: 'USD', symbol: '$', - symbolPosition: 'left', + symbolPosition: 'left' as const, thousandSeparator: ',', decimalSeparator: '.', precision: 2, }; const config = { ...defaultCurrency, ...setting }; + + let precision = config.precision; + if ( precision === null ) { + // eslint-disable-next-line no-console + console.warn( 'Currency precision is null' ); + // eslint-enable-next-line no-console + + precision = NaN; + } else if ( typeof precision === 'string' ) { + precision = parseInt( precision, 10 ); + } + currency = { code: config.code.toString(), symbol: config.symbol.toString(), @@ -45,16 +125,10 @@ const CurrencyFactory = function ( currencySetting ) { decimalSeparator: config.decimalSeparator.toString(), priceFormat: getPriceFormat( config ), thousandSeparator: config.thousandSeparator.toString(), - precision: parseInt( config.precision, 10 ), + precision, }; } - function stripTags( str ) { - const tmp = document.createElement( 'DIV' ); - tmp.innerHTML = str; - return tmp.textContent || tmp.innerText || ''; - } - /** * Formats money value. * @@ -62,7 +136,7 @@ const CurrencyFactory = function ( currencySetting ) { * @param {boolean} [useCode=false] Set to `true` to use the currency code instead of the symbol. * @return {?string} A formatted string. */ - function formatAmount( number, useCode = false ) { + function formatAmount( number: number | string, useCode = false ) { const formattedNumber = numberFormat( currency, number ); if ( formattedNumber === '' ) { @@ -82,7 +156,7 @@ const CurrencyFactory = function ( currencySetting ) { * @param {number|string} number number to format * @return {?string} A formatted string. */ - function formatCurrency( number ) { + function formatCurrency( number: number | string ) { deprecated( 'Currency().formatCurrency', { version: '5.0.0', alternative: 'Currency().formatAmount', @@ -92,31 +166,6 @@ const CurrencyFactory = function ( currencySetting ) { return formatAmount( number ); } - /** - * Get the default price format from a currency. - * - * @param {CurrencyConfig} config Currency configuration. - * @return {string} Price format. - */ - function getPriceFormat( config ) { - if ( config.priceFormat ) { - return stripTags( config.priceFormat.toString() ); - } - - switch ( config.symbolPosition ) { - case 'left': - return '%1$s%2$s'; - case 'right': - return '%2$s%1$s'; - case 'left_space': - return '%1$s %2$s'; - case 'right_space': - return '%2$s %1$s'; - } - - return '%1$s%2$s'; - } - /** * Get formatted data for a country from supplied locale and symbol info. * @@ -126,13 +175,16 @@ const CurrencyFactory = function ( currencySetting ) { * @return {CurrencyConfig | {}} Formatted currency data for country. */ function getDataForCountry( - countryCode, - localeInfo = {}, - currencySymbols = {} - ) { - const countryInfo = localeInfo[ countryCode ] || {}; - const symbol = currencySymbols[ countryInfo.currency_code ]; + countryCode: string, + localeInfo: Record< string, CountryInfo | undefined > = {}, + currencySymbols: Record< string, string | undefined > = {} + ): CurrencyConfig | Record< string, never > { + const countryInfo = localeInfo[ countryCode ]; + if ( ! countryInfo ) { + return {}; + } + const symbol = currencySymbols[ countryInfo.currency_code ]; if ( ! symbol ) { return {}; } @@ -147,6 +199,8 @@ const CurrencyFactory = function ( currencySetting ) { }; } + setCurrency( currencySetting ); + return { getCurrencyConfig: () => { return { ...currency }; @@ -164,7 +218,7 @@ const CurrencyFactory = function ( currencySetting ) { * @param {number|string} number A floating point number (or integer), or string that converts to a number * @return {number} The original number rounded to a decimal point */ - formatDecimal( number ) { + formatDecimal( number: number | string ) { if ( typeof number !== 'number' ) { number = parseFloat( number ); } @@ -185,7 +239,7 @@ const CurrencyFactory = function ( currencySetting ) { * @param {number|string} number A floating point number (or integer), or string that converts to a number * @return {string} The original number rounded to a decimal point */ - formatDecimalString( number ) { + formatDecimalString( number: number | string ) { if ( typeof number !== 'number' ) { number = parseFloat( number ); } @@ -202,7 +256,7 @@ const CurrencyFactory = function ( currencySetting ) { * @param {number|string} number A floating point number (or integer), or string that converts to a number * @return {Node|string} The number formatted as currency and rendered for display. */ - render( number ) { + render( number: number | string ) { if ( typeof number !== 'number' ) { number = parseFloat( number ); } diff --git a/packages/js/currency/src/test/index.js b/packages/js/currency/src/test/index.ts similarity index 89% rename from packages/js/currency/src/test/index.js rename to packages/js/currency/src/test/index.ts index 70aa824414b..3f6ef2529de 100644 --- a/packages/js/currency/src/test/index.js +++ b/packages/js/currency/src/test/index.ts @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import Currency from '../'; +import Currency from '..'; describe( 'formatAmount', () => { it( 'should use defaults (USD) when currency not passed in', () => { @@ -37,7 +37,9 @@ describe( 'formatAmount', () => { it( "should return empty string when given an input that isn't a number", () => { const currency = Currency(); expect( currency.formatAmount( 'abc' ) ).toBe( '' ); + // @ts-expect-error formatAccount expects a number or string; expect( currency.formatAmount( false ) ).toBe( '' ); + // @ts-expect-error formatAccount expects a number or string; expect( currency.formatAmount( null ) ).toBe( '' ); } ); } ); @@ -65,7 +67,9 @@ describe( 'currency.formatDecimal', () => { it( "should return 0 when given an input that isn't a number", () => { const currency = Currency(); expect( currency.formatDecimal( 'abc' ) ).toBe( 0 ); + // @ts-expect-error formatAccount expects a number or string; expect( currency.formatDecimal( false ) ).toBe( 0 ); + // @ts-expect-error formatAccount expects a number or string; expect( currency.formatDecimal( null ) ).toBe( 0 ); } ); } ); @@ -93,7 +97,9 @@ describe( 'currency.formatDecimalString', () => { it( "should return empty string when given an input that isn't a number", () => { const currency = Currency(); expect( currency.formatDecimalString( 'abc' ) ).toBe( '' ); + // @ts-expect-error formatAccount expects a number or string; expect( currency.formatDecimalString( false ) ).toBe( '' ); + // @ts-expect-error formatAccount expects a number or string; expect( currency.formatDecimalString( null ) ).toBe( '' ); } ); } ); diff --git a/packages/js/currency/tsconfig.json b/packages/js/currency/tsconfig.json index 1d13e90e9a6..ea9f201d401 100644 --- a/packages/js/currency/tsconfig.json +++ b/packages/js/currency/tsconfig.json @@ -1,8 +1,10 @@ { "extends": "../tsconfig", "compilerOptions": { - "declaration": true, "rootDir": "src", - "outDir": "build-module" + "outDir": "build-module", + "declaration": true, + "declarationMap": true, + "declarationDir": "./build-types" } }