O.OptionalKeys<O> is returning a set of keys that includes required keys, not just optional keys in TypeScript 3.7.3.
O.RequiredKeys<O> has a similar issue in always returning as never.
type Test = {
hello: string;
world?: string;
}
// Should be "hello", but is "hello" | "world"
type TestOptionalKeys = O.OptionalKeys<Test>
// Should be "world", but is never
type TestRequiredKeys = O.RequiredKeys<Test>
I expect only the set of keys in a type marked optional (?) to be returned.
TypeScript: 3.7.3
ts-toolbelt: 4.14.0
VSCode: 1.41.0
Node.js: 12.4.0
Hi @imahassle,
Everything seems right on my side
type Test = {
hello: string;
world?: string;
}
Optional key here is "world" and
Required key here is "hello"
In your sentence, it is the other way around
type OptionalKeys = O.OptionalKeys<Test> // world
type RequiredKeys = O.RequiredKeys<Test> // hello
Ah, yes I did get it flipped around.
But the issue persists locally for me:

Strange, I'm also on:
TypeScript: 3.7.3
ts-toolbelt: 4.14.0
VSCode: 1.41.0
Node.js: 12.4.0
Are you using VSCode? obvioulsy
If so:
Did this work?
Nuked my node_modules and package-lock.json and re-installed, same issue.
Do my TS libraries make a difference?
Part of my tsconfig.json:
{
"compilerOptions": {
"target": "ES2016",
"lib": ["dom", "ES2016", "ES2018", "es2017.object", "scripthost", "dom.iterable"]
}
}
I'm not aware of anything else that may make a difference.
Found the culprit. Please enable:
"strict": true,
If you can do that for now. I'm going to investigate why this happens exactly.
Thank you for reporting this bug!
Oh that worked for me! We currently don't have strict turned on for our project. I'll have to see if it breaks anything on our side 馃槵
Thanks for the fast response!
And thanks for this awesome project! It's super helpful. 馃榿
Thanks for the compliments! Now thanks to you, we can see there are lots of tests failing. Gotta work on it:
tst/List.ts:56:5 - error TS2322: Type '0' is not assignable to type '1'.
56 check<T.AssignUp<[1], [[2, 1], [3, 2?]]>, [3, 2 | 1], Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/List.ts:116:5 - error TS2322: Type '0' is not assignable to type '1'.
116 check<T.Either<T_EITHER, '0' | '1'>, EITHER_T_01, Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/List.ts:278:5 - error TS2322: Type '0' is not assignable to type '1'.
278 check<T.Intersect<T, T1, 'equals'>, INTERSECT_T_T1_NUMBER_EQUALS, Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/List.ts:463:5 - error TS2322: Type '0' is not assignable to type '1'.
463 check<T.Prepend<never, [4, 5]>, never, Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/List.ts:551:5 - error TS2322: Type '0' is not assignable to type '1'.
551 check<T.Tail<never>, never, Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:59:5 - error TS2322: Type '0' is not assignable to type '1'.
59 check<O.AssignUp<O_ASSIGNUP, Os_ASSIGNUP>, ASSIGNUP_O_Os, Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:98:5 - error TS2322: Type '0' is not assignable to type '1'.
98 check<O.CompulsoryKeys<O>, COMPULSORYKEYS_O, Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:172:5 - error TS2322: Type '0' is not assignable to type '1'.
172 check<O.Either<O_EITHER, 'a' | 'b'>, EITHER_O_AB_TRUE, Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:267:5 - error TS2322: Type '0' is not assignable to type '1'.
267 check<O.Filter<O, string, 'extends->'>, FILTER_O_DEFAULT, Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:279:5 - error TS2322: Type '0' is not assignable to type '1'.
279 check<O.FilterKeys<O, string, 'extends->'>, FILTERKEYS_O_DEFAULT, Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:295:5 - error TS2322: Type '0' is not assignable to type '1'.
295 check<O.Has<O, 'd', string, 'extends->'>, 0 | 1, Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:296:5 - error TS2322: Type '0' is not assignable to type '1'.
296 check<O.Has<O, 'd', 'string0', 'equals'>, 0, Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:298:5 - error TS2322: Type '0' is not assignable to type '1'.
298 check<O.Has<O, 'd', undefined, 'extends->'>, 0 | 1, Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:299:5 - error TS2322: Type '0' is not assignable to type '1'.
299 check<O.Has<O, 'd', 'string0', 'equals'>, 0, Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:305:5 - error TS2322: Type '0' is not assignable to type '1'.
305 check<O.Has<O, 'a' | 'd', string, 'extends->'>, 0 | 1, Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:440:5 - error TS2322: Type '0' is not assignable to type '1'.
440 check<O.Merge<O1, O>, MERGE_O1_O, Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:579:5 - error TS2322: Type '0' is not assignable to type '1'.
579 check<O.MergeUp<O_MERGEUP, O1_MERGEUP>, MERGEUP_O_O1, Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:580:5 - error TS2322: Type '0' is not assignable to type '1'.
580 check<O.MergeUp<O_MERGEUP, O1_MERGEUP, 'deep'>, MERGEUP_O_O1_DEEP, Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:630:5 - error TS2322: Type '0' is not assignable to type '1'.
630 check<O.NonNullableKeys<O>, 'a' | 'b' | 'c' | 'f' | 'g' | 'k', Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:672:5 - error TS2322: Type '0' is not assignable to type '1'.
672 check<O.NullableKeys<O>, 'd' | 'e' | 'h' | 'j', Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:732:5 - error TS2322: Type '0' is not assignable to type '1'.
732 check<O.OptionalKeys<O>, 'd' | 'e' | 'h', Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:815:5 - error TS2322: Type '0' is not assignable to type '1'.
815 check<O.PathValid<any, ['a', 'a']>, ['a', 'a'], Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:815:11 - error TS2589: Type instantiation is excessively deep and possibly infinite.
815 check<O.PathValid<any, ['a', 'a']>, ['a', 'a'], Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:819:5 - error TS2322: Type '0' is not assignable to type '1'.
819 check<O.PathValid<O_PATHVALID, ['b', 'b', 0]>, ['b', 'b', 0], Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:819:11 - error TS2589: Type instantiation is excessively deep and possibly infinite.
819 check<O.PathValid<O_PATHVALID, ['b', 'b', 0]>, ['b', 'b', 0], Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Object.ts:971:5 - error TS2322: Type '0' is not assignable to type '1'.
971 check<O.RequiredKeys<O>, 'a' | 'b' | 'c' | 'f' | 'g' | 'j' | 'k', Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tst/Union.ts:152:5 - error TS2322: Type '0' is not assignable to type '1'.
152 check<U.Strict<{a: 0} | {b: 0}>, {a: 0, b?: undefined} | {a?: undefined, b: 0}, Test.Pass>(),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Yup, our project isn't ready to go with strict checking, though it's a work in progress.
For now, my workaround is to resurrect the old version of OptionalKeys:
type OptionalKeys<T extends object> = { [K in keyof T]-?: {} extends Pick<T, K> ? K : never }[keyof T]
Ok, you can update to the latest version and:
"strictNullChecks": true
And if you still cannot enable this, good news:
Only NonNullableKeys, CompulsoryKeys & NullableKeys are affected
Cheers