-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Add a new type Required #15012
Comments
type Required<T> = T & {
[P in keyof T]: T[P];
}
let a: Required<{ x: number }> = { }; // Property 'x is missing in type '{}' it seems to work well. |
@monolithed That snippet doesn't have any optional properties in the type you're passing as a type argument to type Required<T> = T & {
[P in keyof T]: T[P];
}
let a: Required<{ x?: number }> = { }; |
@masaeedu, oh, you're right ( |
This is what you are looking for I believe. #13224 |
So, why the following operation |
@monolithed type RequiredInternal<T, K extends keyof T> = { [P in K]: T[P] }; // Use type parameter to force it not to be considered homomorphic
type Required<T> = T & RequiredInternal<T, keyof T>;
let a: Required<{ x?: number }> = { }; // => Property 'x' is missing in type '{}' |
@PyroVortex No, I tried that yesterday (I think it might have been your comment on another issue). With // tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"strictNullChecks": true,
"noImplicitAny": false,
"sourceMap": false
}
} // main.ts
type RequiredInternal<T, K extends keyof T> = { [P in K]: T[P] }; // Use type parameter to force it not to be considered homomorphic
type Required<T> = T & RequiredInternal<T, keyof T>;
let a: Required<{ x?: number }> = { }; // No error here
let x: number = a.x; // Error here because number | undefined is not assignable to number The expected error does not appear, and |
@masaeedu In general, we lack an ability to remove a type from a union using pure type manipulation (we can do it with an actual value). |
@PyroVortex How did you enable |
@PyroVortex @masaeedu I am too trying to create type that maps optional fields to required fields. I get the example above to raise an error with TS 2.1.4 and in the playground (which it seems to be using). From 2.1.5 and up it does not give me an error anymore. Any idea why it is so? |
This feature is very handy in react stateful components when you want to specify the default state, and you want to make sure you don't miss any state. |
I'm still running into issues with this in TS 2.4.1. I've tried breaking the homomorphic relationship, as described in #13723, and I believe this does that because the fields are now required, but instead of removing all traces of optional parameters it keeps the type union with type RequiredInternal<T extends {[key: string]: any}, K extends string> = { [P in K]: T[P]};
type Required<T> = RequiredInternal<T, keyof T>;
// As expected: No compile error
const val1: Required<{x?: number}> = {x: 1};
// As expected: Compile error:
// TS2322: Type '{}' is not assignable to type 'RequiredInternal<{ x?: number | undefined; }, "x">'. Property 'x' is missing in type '{}'.
const val2: Required<{x?: number}> = {};
// Not expected?: No compile error
// Expected something like TS2322: Type '{ x: undefined; }' is not assignable to type 'RequiredInternal<{ x?: number | undefined; }, "x">'. Types of property 'x' are incompatible. Type 'undefined' is not assignable to type 'number'.
const val3: Required<{x?: number}> = {x: undefined}; |
@PyroVortex I'm afraid even with strictNullCheck, your example still results in a being optional (typescript 2.4): |
Try this:
|
@sandersn @weswigham Is there a better way to do this? |
I want to say... no? @ssonne seems to have it. The core is breaking the inference of a homomorphic relationship within the mapped type; and it seems like we keep that for as long as we can (traversing aliases and simplifying unions to look for it). The intersection-index indirection seems like a sound way to go. |
@monolithed why don't you make PR --or better yet, invite @ssonne to do it because he figured it out-- to add |
The PR is closed due to:
|
@ssonne I think this is a bug, and I might have a fix in mind... so... I'm sorry? |
@DanielRosenwasser So you’re saying this |
I use one of the variants of the aforementioned Thankfully, there are a few tickets out for "subtraction" types which might solve this: #4183, #12215, #15059. |
I just want to get clarification on this @DanielRosenwasser. Is the fact that this type works a quirk of a bug you think that @ssonne has found? So if we were to adopt this, it would stop working once the bug is fixed? |
The workaround @ssonne has doesn't work in all contexts, unfortunately, too. For example, if you enable interface Optional {
foo?: () => {}
}
type Strict = Required<Optional>
const impl: Strict = {
foo() {}
};
impl.foo();
// ^ Object is possibly 'undefined'. |
We can make required properties, but they still have nullable values. The last piece is NonNullable type. type Required<T> = {
[P in Purify<keyof T>]: NonNullable<T[P]>;
};
type Purify<T extends string> = { [P in T]: T; }[T];
type NonNullable<T> = T - undefined - null; |
Well, right now we can have some workaround that allows you to define required type from partial one type PartialPerson = {
name? : number;
surname? : string;
}
declare function makeRequired<T>(notRequired : Partial<T>): T;
let __requiredPersonVariable = null as any && makeRequired({} as PartialPerson);
type RequiredPerson = typeof __requiredPersonVariable; Then if we got #6606 we could do something like this (I guess, I'm not sure how it would work with generic type aliases if it was implemented): type Required<T> = typeof( null as any && makeRequired({} as T)) For me It's another example how useful #6606 would be if we had it :) |
I've found out that type Required<T> = {
[P in Purify<keyof T>]: NonNullable<T[P]>;
};
type Purify<T extends string> = { [P in T]: T; }[T];
type NonNullable<T> = T & {}; Thanks to @falsandtru for the Purify method. See: Playground (remember to enable strictNullChecks). |
Great! @weswigham @sandersn @ahejlsberg Can we use |
Very nice @niieani! And it makes sense: |
For those interested, I've added |
Now the latest version is: type Required<T> =
T extends object
? { [P in Purify<keyof T>]: NonNullable<T[P]>; }
: T;
type DeepRequired<T, U extends object | undefined = undefined> =
T extends object
? { [P in Purify<keyof T>]: NonNullable<T[P]> extends NonNullable<U | Function | Class> ? NonNullable<T[P]> : DeepRequired<NonNullable<T[P]>, U>; }
: T; This type can stop the processing with the specified object types like https://github.com/falsandtru/spica/blob/master/src/lib/type.ts |
@falsandtru And as of today there's no need for type Required<T> =
T extends object
? { [P in keyof T]-?: NonNullable<T[P]>; }
: T;
type DeepRequired<T, U extends object | undefined = undefined> =
T extends object
? { [P in keyof T]-?: NonNullable<T[P]> extends NonNullable<U | Function | Class> ? NonNullable<T[P]> : DeepRequired<NonNullable<T[P]>, U>; }
: T; this should also handle assignability while things are still generic better, too; or at least that's the goal. |
Right, I'll try to use the new syntax later. |
It would be nice to have a new type that allows property to be required as opposited to
Partial
:The text was updated successfully, but these errors were encountered: