简体   繁体   中英

TypeScript: in recursive type, extra props allowed to be assigned in object declaration only

When creating an object using the following recursive type, somehow extra keys not present in the type are allowed to be added when the object is created (but not afterward). Any thoughts on why this is?

type WithChildProps<Props, ChildProps> = Props & { __childProps__: ChildProps };

type WrappedProps<Props extends Record<string, any>, WrapperPropsTuple extends any[]> = WrapperPropsTuple extends [infer ParentProps, ...infer ChildProps]
  ? ChildProps extends []
    ? WithChildProps<ParentProps, Props>
    : WithChildProps<ParentProps, WrappedProps<Props, ChildProps>>
  : string;



type ObjTuple = [
  { prop1: string },
  { prop2: string },
  { prop3: string },
];

type ObjProps = { mainProp: string };

type ObjWrappedProps = WrappedProps<ObjProps, ObjTuple>;

const obj: ObjWrappedProps = {
  prop1: 'allowed',
  __childProps__: {
    prop2: 'allowed',
    __childProps__: {
      prop3: 'allowed',
      extraProp: 'why is this allowed?', // Should error but doesn't
      __childProps__: {
          mainProp: 'allowed',
          extraProp: 'and this also?', // Should error but doesn't
      },
    },
  },
};

// Errors as expected
obj.extraProp = 'expected error';
obj.__childProps__.__childProps__.extraProp = 'expected error';

While I'm unable to explain this behavior, I can provide you with working utility type.

Consider this example:

type ObjTuple = [
  { prop1: string },
  { prop2: string },
  { prop3: string },
];

type ObjProps = { mainProp: string };

type Key = '__childProps__';

type Builder<
  Tuple extends any[],
  Level extends number[] = [],
  Result = {}> =
  Level['length'] extends Tuple['length']
  ? Result & ObjProps
  : Tuple[Level['length']] & {
    [Prop in Key]:
    Builder<Tuple, [...Level, 1], Result>
  }


// ok
const test: Builder<ObjTuple> = {
  prop1: 'allowed',
  __childProps__: {
    prop2: 'allowed',
    __childProps__: {
      prop3: 'allowed',
      __childProps__: {
        mainProp: 'allowed',
      },
    },
  },
};

const test2: Builder<ObjTuple> = {
  prop1: 'allowed',
  __childProps__: {
    prop22: 'allowed', // expected error
    __childProps__: {
      prop3: 'allowed',
      __childProps__: {
        mainProp: 'allowed',
      },
    },
  },
};

const test3: Builder<ObjTuple> = {
  prop1: 'allowed',
  __childProps__: {
    prop2: 'allowed',
    __childProps__: {
      prop3: 'allowed',
      __childProps__: {
        mainProp2: 'allowed', // expected error
      },
    },
  },
}

Playground

Builder - iterates through only one property __childProps__ while Level tuple length less than original tuple ObjTuple . IF length is equal - return Result merged with ObjProps , otherwise: merge prop from tuple by index Tuple[Level['length']] with recursive iteration through __childProps__ .

As you might have noticed, I increase Level tuple each time by 1 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM