镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Add a new type Required #15012

Description

@monolithed

It would be nice to have a new type that allows property to be required as opposited to Partial:

interface X {
    x?: string 
}

Required<X>{ }; // Property 'x is missing in type '{}'

Activity

  1. masaeedu commented on Apr 5, 2017

    @masaeedu
    Contributor

    This will probably depend on #13253 or #12215, whichever ends up getting implemented. You need a mapped type where you subtract undefined and null.

  2. monolithed commented on Apr 5, 2017

    @monolithed
    Author

    Asad Saeeduddin (@masaeedu)

    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.

  3. masaeedu commented on Apr 5, 2017

    @masaeedu
    Contributor

    AA (@monolithed) That snippet doesn't have any optional properties in the type you're passing as a type argument to Required. This corrected snippet doesn't have the expected error:

    type Required<T> = T & {
      [P in keyof T]: T[P];
    }
    
    let a: Required<{ x?: number }> = { };
  4. monolithed commented on Apr 5, 2017

    @monolithed
    Author

    Asad Saeeduddin (@masaeedu), oh, you're right (

  5. aluanhaddad commented on Apr 5, 2017

    @aluanhaddad
    Contributor

    This is what you are looking for I believe. #13224

  6. monolithed commented on Apr 5, 2017

    @monolithed
    Author

    This is what you are looking for I believe. #13224

    So, why the following operation [P in keyof T]: T[P] is not working as expected?

  7. PyroVortex commented on Apr 5, 2017

    @PyroVortex

    AA (@monolithed)
    The operation is a homomorphic mapping, which have been configured to preserve optional and readonly modifiers. To get the desired behavior, you would need to do:

    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 '{}'
  8. masaeedu commented on Apr 5, 2017

    @masaeedu
    Contributor

    PyroVortex No, I tried that yesterday (I think it might have been your comment on another issue). With tsc version 2.2.1, and the following files:

    // 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 a is typed as having a potentially undefined x property (number | undefined).

  9. PyroVortex commented on Apr 5, 2017

    @PyroVortex

    Asad Saeeduddin (@masaeedu)
    Huh. In the current version in the TypeScript Playground with strictNullChecks enabled, I get an error for the first assignment (missing property), but I do still see the error you are mentioning for the second.

    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).

  10. masaeedu commented on Apr 5, 2017

    @masaeedu
    Contributor

    PyroVortex How did you enable strictNullChecks in playground?

  11. aluanhaddad commented on Apr 6, 2017

    @aluanhaddad
    Contributor

    image

  12. priithaamer commented on Apr 12, 2017

    @priithaamer

    PyroVortex Asad Saeeduddin (@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?

  13. thehappycoder commented on Jun 9, 2017

    @thehappycoder

    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.

  14. mikejhill commented on Jul 11, 2017

    @mikejhill

    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 undefined. Is this intentional?

    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};
  15. 14 remaining items

  16. nevir commented on Nov 14, 2017

    @nevir

    The workaround Samuel Sonne (@ssonne) has doesn't work in all contexts, unfortunately, too. For example, if you enable strictNullChecks:

    interface Optional {
      foo?: () => {}
    }
    type Strict = Required<Optional>
    
    const impl: Strict = {
      foo() {}
    };
    
    impl.foo();
    // ^ Object is possibly 'undefined'.

    [Playground]

  17. falsandtru commented on Nov 16, 2017

    @falsandtru
    Contributor

    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;
  18. mpawelski commented on Nov 16, 2017

    @mpawelski

    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 :)

  19. niieani commented on Nov 22, 2017

    @niieani

    I've found out that T & {} simplifies to non-nullable type, i.e.:

    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 (@falsandtru) for the Purify method.

    See: Playground (remember to enable strictNullChecks).

  20. falsandtru commented on Nov 23, 2017

    @falsandtru
    Contributor

    Great! Wesley Wigham (@weswigham) Nathan Shively-Sanders (@sandersn) Anders Hejlsberg (@ahejlsberg) Can we use type NonNullable<T> = T & {}; as a stable API?

  21. pelotom commented on Nov 23, 2017

    @pelotom

    Very nice Bazyli Brzóska (@niieani)! And it makes sense: {} contains all values except undefined and null, so intersecting with it excludes those and nothing else.

  22. pelotom commented on Nov 28, 2017

    @pelotom

    For those interested, I've added Required (and NonNullable) to Type Zoo.

  23. falsandtru commented on Feb 6, 2018

    @falsandtru
    Contributor

    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 DeepRequired<obj, Element>.

    https://github.com/falsandtru/spica/blob/master/src/lib/type.ts
    https://github.com/falsandtru/spica/blob/master/src/lib/type.test.ts

  24. added
    FixedA PR has been merged for this issue
    and removed
    Needs ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.
    on Feb 13, 2018
  25. weswigham commented on Feb 14, 2018

    @weswigham
    Member

    falsandtru (@falsandtru) And as of today there's no need for Purify:

    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.

  26. falsandtru commented on Feb 14, 2018

    @falsandtru
    Contributor

    Right, I'll try to use the new syntax later.

  27. locked and limited conversation to collaborators on Jul 3, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    FixedA PR has been merged for this issueSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions