-
-
Notifications
You must be signed in to change notification settings - Fork 647
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(typescript): Add strictKeyChecks
option to enforce checking key existence when defaultValue
is used
#2274
Conversation
Nice... |
fixed and added the test. could you please recheck @marcalexiei? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fixes the previous issue,
however now InterpolationMap
is no longer considered when type checking t
options:
Testcase (this time the additional describe block is good):
describe('interpolation values with strictKeyChecks == true', () => {
const t = (() => '') as unknown as TFunction<'interpolators'>;
it('single interpolation value', () => {
expectTypeOf(t('simple', { val: 'val' })).toMatchTypeOf<string>();
// @ts-expect-error val interpolation value is missing
expectTypeOf(t('simple', { notPresent: 'notPresent' })).toMatchTypeOf<string>();
});
it('multiple interpolation values', () => {
type Expected = 'This has {{more}} than {{one}}';
expectTypeOf(t('simple_multiple_keys', { more: '', one: '' })).toEqualTypeOf<Expected>();
// @ts-expect-error "more" required key is missing
expectTypeOf(t('simple_multiple_keys', { less: '', one: '' })).toEqualTypeOf<Expected>();
});
});
Note
You need to include interpolators
namespace in test/typescript/strict-key-checks/i18next.d.ts
import 'i18next'; import { TestNamespaceCustom, TestNamespaceCustomAlternate, TestNamespaceFallback, + TestNamespaceInterpolators, } from '../test.namespace.samples'; declare module 'i18next' { interface CustomTypeOptions { strictKeyChecks: true; defaultNS: 'custom'; fallbackNS: 'fallback'; resources: { custom: TestNamespaceCustom; alternate: TestNamespaceCustomAlternate; fallback: TestNamespaceFallback; + interpolators: TestNamespaceInterpolators; }; } }
}); | ||
}); | ||
|
||
describe('t defaultValue with strictKeyChecks == true and `returnObjects`', () => { | ||
const t = (() => '') as unknown as TFunction<'alternate'>; | ||
|
||
it('should work alongside `returnObjects`', () => { | ||
expectTypeOf(t('foobar', { returnObjects: true })).toMatchTypeOf<{ | ||
barfoo: 'barfoo'; | ||
deep: { | ||
deeper: { | ||
deeeeeper: 'foobar'; | ||
}; | ||
}; | ||
}>(); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stylistic issue: please move this test inside the describe block above. No need to have another block.
I hadn't released that there is not need for an additional block.
}); | |
}); | |
describe('t defaultValue with strictKeyChecks == true and `returnObjects`', () => { | |
const t = (() => '') as unknown as TFunction<'alternate'>; | |
it('should work alongside `returnObjects`', () => { | |
expectTypeOf(t('foobar', { returnObjects: true })).toMatchTypeOf<{ | |
barfoo: 'barfoo'; | |
deep: { | |
deeper: { | |
deeeeeper: 'foobar'; | |
}; | |
}; | |
}>(); | |
}); | |
}); | |
}); | |
it('should work alongside `returnObjects`', () => { | |
expectTypeOf(t('foobar', { returnObjects: true })).toMatchTypeOf<{ | |
barfoo: 'barfoo'; | |
deep: { | |
deeper: { | |
deeeeeper: 'foobar'; | |
}; | |
}; | |
}>(); | |
}); | |
}); |
You're right. I think the current solution is too 'hacky' and therefore break other things... Let me think of a cleaner solution for a bit |
This comment was marked as outdated.
This comment was marked as outdated.
export type TFunction< | ||
Ns extends Namespace = DefaultNamespace, | ||
KPrefix = undefined, | ||
> = _StrictKeyChecks extends true ? TFunctionStrict<Ns, KPrefix> : TFunctionNonStrict<Ns, KPrefix>; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still not sure what are the possible implications of converting this interface
into a type
(although it will eventually resolve into an interface)
(also the naming)
@masnormen you requested my review but you are still pushing commits, request a review when you have finished. |
Sorry @marcalexiei, my bad! I'm trying to make sure there are no perf issues and got some problem with vitest. Should be done now. By the way, I followed the tutorial on CONTRIBUTING.md to test multiple tsconfig within the same scenario. But it turns out that Vitest will do multiple writes into the same The temp file name also seems to be hardcoded here so can't really do anything atm. So for now I just copied the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry @marcalexiei, my bad!
No problem! I haven't started the PR review yet, but better to avoid push after requesting a review😉
I'm trying to make sure there are no perf issues
Looks like you did it ✨!
Done a few benchmarks on my biggest package in a mono-repo
Here are the results of running tsc --noEmit
:
With current types | With your fix |
---|---|
18.479s | 18.610s |
19.948s | 18.420s |
19.077s | 18.698s |
18.479s | 18.804s |
I experienced similar results in test/typescript/many-keys
test scenarios!
By the way, I followed the tutorial on CONTRIBUTING.md to test multiple tsconfig within the same scenario. But it turns out that Vitest will do multiple writes into the same tsconfig.vitest-temp.json file when there are multiple tsconfig.*.json, resulting in a broken temp tsconfig JSON and therefore broken test run:
I know, that why I used the singular sentence:
better to add a warning to avoid this kind of situation in the future.
Thanks for bringing this up!
I'll fix this issue in a separate PR
So for now I just copied the many-keys folder and adjusted many-keys-strict to check against the strictKeyChecks: true option.
I'm going to create a PR on your fork to avoid namespace duplication and add a warning regarding testing vite with multiple .--project
flags. After that we can merge this
refactor: dedupe many-keys namespace and PERF_README files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I created an issue on vitest repository about the tsconfig.vitest-temp.json
:
vitest-dev/vitest#7107
Anyway if the CI will be green this can be merged
Thanks to both... |
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [i18next](https://www.i18next.com) ([source](https://github.com/i18next/i18next)) | dependencies | major | [`23.16.8` -> `24.2.0`](https://renovatebot.com/diffs/npm/i18next/23.16.8/24.2.0) | --- ### Release Notes <details> <summary>i18next/i18next (i18next)</summary> ### [`v24.2.0`](https://github.com/i18next/i18next/blob/HEAD/CHANGELOG.md#2420) [Compare Source](i18next/i18next@v24.1.2...v24.2.0) - feat(typescript): Add strictKeyChecks option to enforce checking key existence when defaultValue is used [2274](i18next/i18next#2274), fixes [2043](i18next/i18next#2043) ### [`v24.1.2`](https://github.com/i18next/i18next/blob/HEAD/CHANGELOG.md#2412) [Compare Source](i18next/i18next@v24.1.1...v24.1.2) - optimize fix: Bug Report: Unsafe Behavior in i18n.t Function Leading to Potential Code Execution [2273](i18next/i18next#2273) ### [`v24.1.1`](https://github.com/i18next/i18next/blob/HEAD/CHANGELOG.md#2411) [Compare Source](i18next/i18next@v24.1.0...v24.1.1) - fix: Bug Report: Unsafe Behavior in i18n.t Function Leading to Potential Code Execution [2273](i18next/i18next#2273) ### [`v24.1.0`](https://github.com/i18next/i18next/blob/HEAD/CHANGELOG.md#2410) [Compare Source](i18next/i18next@v24.0.5...v24.1.0) - try to address [2270](i18next/i18next#2270) by cloning the store data [2271](i18next/i18next#2271) ### [`v24.0.5`](https://github.com/i18next/i18next/blob/HEAD/CHANGELOG.md#2405) [Compare Source](i18next/i18next@v24.0.4...v24.0.5) - remove extra log for [2268](i18next/i18next#2268) ### [`v24.0.4`](https://github.com/i18next/i18next/blob/HEAD/CHANGELOG.md#2404) [Compare Source](i18next/i18next@v24.0.3...v24.0.4) - simplify fix: incorrect locale detected [2268](i18next/i18next#2268) ### [`v24.0.3`](https://github.com/i18next/i18next/blob/HEAD/CHANGELOG.md#2403) [Compare Source](i18next/i18next@v24.0.2...v24.0.3) - fix: incorrect locale detected [2268](i18next/i18next#2268) - fix: Intl.getCanonicalLocales throws with custom regions [2267](i18next/i18next#2267) ### [`v24.0.2`](https://github.com/i18next/i18next/blob/HEAD/CHANGELOG.md#2402) [Compare Source](i18next/i18next@v24.0.1...v24.0.2) - if no Intl api, log error and use dummy rule ### [`v24.0.1`](https://github.com/i18next/i18next/blob/HEAD/CHANGELOG.md#2401) [Compare Source](i18next/i18next@v24.0.0...v24.0.1) - early return and log error, if no Intl api ### [`v24.0.0`](https://github.com/i18next/i18next/blob/HEAD/CHANGELOG.md#2400) [Compare Source](i18next/i18next@v23.16.8...v24.0.0) **This is a major breaking release:** - remove support for older environments - remove old i18next JSON formats - To convert your existing v3 translations to the v4 format, have a look at [i18next-v4-format-converter](https://github.com/i18next/i18next-v4-format-converter) or this [web tool](https://i18next.github.io/i18next-v4-format-converter-web/). - remove support for compatibility to v1 API - Intl API is mandatory now and will not fallback anymore - possible compatibility layer for older formats: `test/compatibility/v4/v4Compatibility.js` - rename `initImmediate` to `initAsync` - fallback to `dev` language if plural rule not found - remove TypeScript v4 support. TypeScript v5 is now an optional peer dependency - addresses - [2244](i18next/i18next#2244) - [2184](i18next/i18next#2184) - [2213](i18next/i18next#2213) - [2206](i18next/i18next#2206) - [2208](i18next/i18next#2208) - [2148](i18next/i18next#2148) - [2254](i18next/i18next#2254) ➡️ check out the [migration guide](https://www.i18next.com/misc/migration-guide#v23.x.x-to-v24.0.0) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS41Ny40IiwidXBkYXRlZEluVmVyIjoiMzkuNzUuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Reviewed-on: https://git.tristess.app/alexandresoro/ouca/pulls/384 Reviewed-by: Alexandre Soro <code@soro.dev> Co-authored-by: renovate <renovate@git.tristess.app> Co-committed-by: renovate <renovate@git.tristess.app>
In my experience, I use
defaultValue
for quick prototyping, but sometimes I forgot to add the actual key to my translation JSON file and it goes unnoticed because it doesn't produce errors.This PR introduces a new option
strictKeyChecks
, which is customizable via user-augmentedCustomTypeOptions
interface. Enabling this option will make it illegal to reference a key that doesn't exist, even whendefaultValues
are provided.If accepted, this PR closes #2043
Examples:
without strictKeyChecks
with strictKeyChecks = true
Checklist
npm run test
Checklist (for documentation change)