代码之家  ›  专栏  ›  技术社区  ›  Tom Crockett

typescript参数类型推断失败

  •  10
  • Tom Crockett  · 技术社区  · 6 年前

    我创造了一个 bug report 但也许有人有一个解决办法。基本上,我想根据另一个函数的类型,有条件地为其提供一些参数:

    declare function foo<P>(params: { f: (p: P) => void } & P): void;
    
    foo({
      f(params: { bar(x: number): void }) {},
      bar(x) {},
    });
    

    所以有一个特殊的参数 f 它具有函数类型,并且函数期望的任何属性都必须作为参数包含在 foo .这里的参数 bar 推断为 any 但应该是 number .

    2 回复  |  直到 6 年前
        1
  •  2
  •   Titian Cernicova-Dragomir    6 年前

      class Foo<P = { fn : (e: string)=> void }> extends React.Component<P>{}
      let foo = () => (
        <div>
          <Foo fn={e=> e}/> {/* e is any */}
        </div>
      )
    

    P

    // Do not change { __default?:true } to DefaultGenericParameter it only works with the former for some reason
    type IfDefaultParameter<C, Default, NonDefault> = C extends { __default?:true } ? Default : NonDefault
    type DefaultGenericParameter = { __default? : true; }
    
    export type InferredProps<C extends React.ReactType> =
      C extends React.ComponentType<infer P> ? P :
      C extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[C] :
      {};
    
    // The props for Foo, will extract props from the generic parameter passed in 
    type FooProps<T extends React.ReactType = React.ComponentClass<{ fn: (s: string )=> void }>> = InferredProps<T> & {
      other?: string
    }
    
    // Actual component class
    class _Foo<P> extends React.Component<P>{}
    
    // The public facing constructor 
    const Foo : {
      new <P extends React.ReactType & {__default?: true} = React.ComponentClass<{ fn: (s: string )=> void }> & DefaultGenericParameter>(p: P) :
      IfDefaultParameter<P, _Foo<FooProps>, _Foo<FooProps<P>>>
    } = _Foo as any;
    
      let foo = () => (
        <div>
          <Foo fn={e=> e}/> {/* e is string */}
          <Foo<React.ComponentClass<{ fn: (s: number )=> void }>> fn={e=> e}/> {/* e is number */}
        </div>
      )
    

    Avatar

    export type AvatarProps<C extends AnyComponent = AnyComponent> = StandardProps<
      PassthruProps<C, 'div'>,
      AvatarClassKey
    > & {
      alt?: string;
      childrenClassName?: string;
      component?: C;
      imgProps?: React.HtmlHTMLAttributes<HTMLImageElement>;
      sizes?: string;
      src?: string;
      srcSet?: string;
    };
    
    type IfDefaultParameter<C, Default, NonDefault> = C extends { __default?:true } ? Default : NonDefault
    type DefaultGenericParameter = { __default? : true; }
    
    declare const Avatar : {
      new <C extends AnyComponent & DefaultGenericParameter = 'div' & DefaultGenericParameter>(props: C) 
      : IfDefaultParameter<C, React.Component<AvatarProps<'div'>>, React.Component<AvatarProps<C>>>
    }
    

    const AvatarTest = () => (
      <div>
        {/* e is React.MouseEvent<HTMLDivElement>*/}
        <Avatar onClick={e => log(e)} alt="Image Alt" src="example.jpg" />
        {/* e is {/* e is React.MouseEvent<HTMLButtonElement>*/}*/}
        <Avatar<'button'> onClick={e => log(e)} component="button" alt="Image Alt" src="example.jpg" />
        <Avatar<React.SFC<{ x: number }>>
          component={props => <div>{props.x}</div>} // props is props: {x: number;} & {children?: React.ReactNode;}
          x={3} // ok 
          // IS NOT allowed:
          // onClick={e => log(e)}
          alt="Image Alt"
          src="example.jpg"
        />
      </div>
    

        2
  •  4
  •   artem    6 年前

    declare function foo(params: { tag: 'a', bar(x: number): void }): void;
    declare function foo(params: { tag: 'b', bar(x: boolean): void }): void;
    
    foo({ tag: 'a', bar(x) { console.log('x =', x)} }); // (parameter) x: number
    

    foo f

    declare function foo<P>(params: { f: (p: P) => void } & P): void;
    
    foo({
      f(params: { bar(x: number): void }) {},
      bar(x) {},
    });
    

    type ComposedProps<InnerProps, N extends string> = 
           InnerProps & { [n in N]: (props: InnerProps) => void };  
    
    // Also I assume in reality it does not return `void` but `React.Element`,
    //  but I don't think it matters
    
    declare function foo<P>(params: P): void;
    
    // foo generic parameter is given explicitly,
    //  which allows to infer (and not repeat) parameter types for `f` and `bar` 
    foo<ComposedProps<{bar(x: number): void}, 'f'>>({
      f(params) {}, // params: { bar(x: number): void; }
      bar(x) {}, // (parameter) x: number
    });