Typescript 条件返回类型

Typescript 条件返回类型,typescript,Typescript,我创造了。这是一个使用JSX语法创建DOM元素的小型库。它是用打字稿写的 现在我想扩展它以支持功能组件。我做了很多修改,但找不到定义正确类型注释的方法。这就是我的结局: const h = < T extends (keyof HTMLElementTagNameMap) | ((props?: P, children?: Child[]) => Node), P extends object >( tag: T, props?: T extends (keyo

我创造了。这是一个使用JSX语法创建DOM元素的小型库。它是用打字稿写的

现在我想扩展它以支持功能组件。我做了很多修改,但找不到定义正确类型注释的方法。这就是我的结局:

const h = <
  T extends (keyof HTMLElementTagNameMap) | ((props?: P, children?: Child[]) => Node),
  P extends object
>(
  tag: T,
  props?: T extends (keyof HTMLElementTagNameMap) ? Props<T> : P,
  ...children: Children[]
): T extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[T] : ReturnType<T> => {
  const childArray: Child[] = [];
  const flattenChildren = (child: Children): void => {
    if (Array.isArray(child)) {
      child.forEach(flattenChildren);
    } else if (child != null && child !== true && child !== false) {
      childArray.push(child);
    }
  };
  flattenChildren(children);
  if (typeof tag !== "string") {
    return tag(props as P, childArray) as ReturnType<T>;
  }
  let ref: Ref<T> | undefined;
  const node = document.createElement(tag);
  if (props) {
    Object.entries(props).forEach(([key, value]) => {
      if (key === "ref") {
        ref = value as Ref<T>;
      } else if (key in node) {
        type Key = keyof typeof node;
        if (node[key as Key] instanceof Object && value instanceof Object) {
          Object.assign(node[key as Key], value);
        } else {
          node[key as Key] = value as typeof node[Key];
        }
      } else {
        node.setAttribute(key, value as string);
      }
    });
  }
  node.append(...(childArray as Node[]));
  if (ref) {
    ref(node);
  }
  return node;
};
const h=<
T扩展(HtmleLementTargetNameMap的键)|((props?:P,children?:Child[])=>节点),
P扩展对象
>(
标签:T,
道具:T扩展(HtmleLementAttagNameMap的键)?道具:P,
…儿童:儿童[]
):T扩展HtmleLementAttagNameMap的键?HTMLElementTagNameMap[T]:返回类型=>{
常量childArray:Child[]=[];
const-Children=(子对象:子对象):void=>{
if(Array.isArray(子级)){
儿童。forEach(儿童);
}else if(child!=null&&child!==true&&child!==false){
childArray.push(child);
}
};
儿童(儿童);
如果(标签类型!=“字符串”){
return标签(props作为P,childArray)作为ReturnType;
}
let ref:ref |未定义;
const节点=document.createElement(标记);
如果(道具){
Object.entries(props.forEach)([key,value])=>{
如果(键==“参考”){
ref=作为ref的值;
}else if(输入节点){
类型键=节点类型的键;
if(对象的节点[键作为键]实例和对象的值实例){
赋值(节点[键作为键],值);
}否则{
节点[键作为键]=值作为节点[键]的类型;
}
}否则{
node.setAttribute(键,值为字符串);
}
});
}
追加(…(作为节点[]的子数组);
如果(参考){
ref(节点);
}
返回节点;
};
如果我只编写类型定义,函数将有2个调用签名:

  • 可以接受HTML标记名和部分DOM节点,并返回映射到给定标记名的DOM节点
  • 另一个将接受一个函数,该函数接受props和children并返回一个DOM节点,并且只使用给定的props和children调用该函数
type Child=number | string |节点;
接口子数组扩展数组{}
类型Children=boolean | null | undefined | Child | ChildrenArray;
接口属性{
//截断的
}
类型Props=属性&
{
[T]]的[K]键?:HtmleLementTableNameMap[T][K]扩展函数
?HTMLElementTagNameMap[T][K]
:部分;
};
导出函数h(
标签:T,
道具?:部分,
…儿童:儿童[]
):HTMLElementTagNameMap[T];
导出功能h节点>(
标签:T,
道具?:参数[0],
…儿童:儿童[]
):返回类型;
目前我遇到以下错误:

h.ts:452:82 - error TS2344: Type 'T' does not satisfy the constraint '(...args: any) => any'.
  Type '"object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 100 more ... | ((props?: P, children?: Child[]) => Node)' is not assignable to type '(...args: any) => any'.
    Type '"object"' is not assignable to type '(...args: any) => any'.

452 ): T extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[T] : ReturnType<T> => {
                                                                                     ~

h.ts:463:5 - error TS2322: Type 'ReturnType<T>' is not assignable to type 'T extends "object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 99 more ... | "wbr" ? HTMLElementTagNameMap[T] : ReturnType<...>'.

463     return tag(props as P, childArray) as ReturnType<T>;
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

h.ts:463:12 - error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '"object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 100 more ... | ((props?: P, children?: Child[]) => Node)' has no compatible call signatures.

463     return tag(props as P, childArray) as ReturnType<T>;
               ~~~~~~~~~~~~~~~~~~~~~~~~~~~

h.ts:463:54 - error TS2344: Type 'T' does not satisfy the constraint '(...args: any) => any'.

463     return tag(props as P, childArray) as ReturnType<T>;
                                                         ~

h.ts:465:16 - error TS2344: Type 'T' does not satisfy the constraint '"object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 99 more ... | "wbr"'.
  Type '"object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 100 more ... | ((props?: P, children?: Child[]) => Node)' is not assignable to type '"object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 99 more ... | "wbr"'.
    Type '(props?: P, children?: Child[]) => Node' is not assignable to type '"object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 99 more ... | "wbr"'.
      Type '(props?: P, children?: Child[]) => Node' is not assignable to type '"wbr"'.
        Type 'T' is not assignable to type '"wbr"'.
          Type '"object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 100 more ... | ((props?: P, children?: Child[]) => Node)' is not assignable to type '"wbr"'.
            Type '"object"' is not assignable to type '"wbr"'.

465   let ref: Ref<T> | undefined;
                   ~

h.ts:466:39 - error TS2345: Argument of type 'T' is not assignable to parameter of type 'string'.
  Type '"object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 100 more ... | ((props?: P, children?: Child[]) => Node)' is not assignable to type 'string'.
    Type '(props?: P, children?: Child[]) => Node' is not assignable to type 'string'.

466   const node = document.createElement(tag);
                                          ~~~

h.ts:470:28 - error TS2344: Type 'T' does not satisfy the constraint '"object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 99 more ... | "wbr"'.
  Type 'T' is not assignable to type '"wbr"'.

470         ref = value as Ref<T>;
h.ts:452:82-错误TS2344:类型“T”不满足约束“(…args:any)=>any”。
键入“object”|“link”|“small”|“sub”|“sup”|“track”|“progress”|“a”|“abbr”|“address”|“applet”|“area”|“article”|“aside”|“audio”|“b”|“base”|“basefont”|“bdi”|。。。还有100多个…|((props?:P,children?:Child[])=>Node)“不可分配给类型“(…args:any)=>any”。
类型“object”不可分配给类型“(…args:any)=>any”。
452):T扩展HtmleLementAttagNameMap的键?HTMLElementTagNameMap[T]:返回类型=>{
~
h、 ts:463:5-错误TS2322:类型“ReturnType”不可分配给类型“T扩展对象”|“链接”|“小”|“sup”|“跟踪”|“进度”|“a”|“abbr”|“地址”|“小程序”|“区域”|“文章”|“旁白”|“音频”|“b”|“基本”|“基本字体”|“基本”| i”|“|“|”|”|更多…|wbr”|返回类型“[HTMLENTAP]:。
463返回标签(道具作为P,childArray)作为返回类型;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
h、 ts:463:12-错误TS2349:无法调用其类型缺少调用签名的表达式。键入“object”|“link”|“small”|“sub”|“sup”|“track”|“progress”|“a”|“abbr”|“address”|“applet”|“area”|“article”|“aside”|“audio”|“b”|“base”|“base”|“base”|“basefont”|“bdi”|“|”|“|“| 100多个…”子节点(=(props?)>children)'没有兼容的呼叫签名。
463返回标签(道具作为P,childArray)作为返回类型;
~~~~~~~~~~~~~~~~~~~~~~~~~~~
h、 ts:463:54-错误TS2344:类型“T”不满足约束“(…args:any)=>any”。
463返回标签(道具作为P,childArray)作为返回类型;
~
h、 ts:465:16-错误TS2344:键入“T”不满足约束“对象”|“链接”|“小”|“子”|“支持”|“跟踪”|“进度”|“a”|“abbr”|“地址”|“小程序”|“区域”|“文章”|“旁白”|“音频”|“b”|“基本”|“基本字体”|“bdi”| 99更多…”。
“对象”类型的“对象”类型的“对象”类型的“对象”类型的“对象”类型的“对象”类型的“对象”类型的“对象”类型的“对象”类型的“对象”类型的“对象”类型的“对象”类型的“对象”地址的“地址”以及“小规模”的“小规模”的“小规模”项目的链接”;“小规模”项目的“小规模”公司”的“小规模”的链接”以及“小规模的“小规模”公司”的”的表面的”以及“小规模的表面”以及“小规模的”以及“小规模的”项目的”以及“小规模的”项目的“文章文章”以及“文章”的文章文章”的文章文章的“除了除了除了除了除了除了除了除了除了除了除了除了除了除了“外,除了除了除了除了除了除了除了”之外之外之外,音频”外,除了除了除了除了除了除了除了除了除了除了除了除了除了除了除了除了“音频”之外之外之外之外之外之外之外之外,音频”外,除了除了除了除了"进步"“a”|“abbr”|“address”|“applet”|“area”|“article”|“aside”|“audio”|“b”|“base”|“basefont”|“bdi”|…99更多…|“wbr””。
Type'(props?:P,children?:Child[])=>Node'不可分配给Type'“object”|“link”|“small”|“sub”|“sup”|“track”|“progress”|“a”|“abbr”|“address”|“applet”|“area”|“article”|“aside”|“audio”|“b”|“base”|“base”|“basefont”| i”|“bdr”|更多…”。
Type'(props?:P,children?:Child[])=>Node'不可分配给类型“wbr”。
类型“T”不可分配给类型“wbr”。
键入“object”|“link”|“small”|“sub”|“sup”|“track”|“progress”|“a”|“abbr”|“address”|“applet”|“area”|“article”|“aside”|“audio”|“b”|“base”|“basefont”|“bdi”|…100多个…”((道具:P,children children children:Child)[])=>节点类型)无法分配给节点类型
h.ts:452:82 - error TS2344: Type 'T' does not satisfy the constraint '(...args: any) => any'.
  Type '"object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 100 more ... | ((props?: P, children?: Child[]) => Node)' is not assignable to type '(...args: any) => any'.
    Type '"object"' is not assignable to type '(...args: any) => any'.

452 ): T extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[T] : ReturnType<T> => {
                                                                                     ~

h.ts:463:5 - error TS2322: Type 'ReturnType<T>' is not assignable to type 'T extends "object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 99 more ... | "wbr" ? HTMLElementTagNameMap[T] : ReturnType<...>'.

463     return tag(props as P, childArray) as ReturnType<T>;
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

h.ts:463:12 - error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '"object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 100 more ... | ((props?: P, children?: Child[]) => Node)' has no compatible call signatures.

463     return tag(props as P, childArray) as ReturnType<T>;
               ~~~~~~~~~~~~~~~~~~~~~~~~~~~

h.ts:463:54 - error TS2344: Type 'T' does not satisfy the constraint '(...args: any) => any'.

463     return tag(props as P, childArray) as ReturnType<T>;
                                                         ~

h.ts:465:16 - error TS2344: Type 'T' does not satisfy the constraint '"object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 99 more ... | "wbr"'.
  Type '"object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 100 more ... | ((props?: P, children?: Child[]) => Node)' is not assignable to type '"object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 99 more ... | "wbr"'.
    Type '(props?: P, children?: Child[]) => Node' is not assignable to type '"object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 99 more ... | "wbr"'.
      Type '(props?: P, children?: Child[]) => Node' is not assignable to type '"wbr"'.
        Type 'T' is not assignable to type '"wbr"'.
          Type '"object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 100 more ... | ((props?: P, children?: Child[]) => Node)' is not assignable to type '"wbr"'.
            Type '"object"' is not assignable to type '"wbr"'.

465   let ref: Ref<T> | undefined;
                   ~

h.ts:466:39 - error TS2345: Argument of type 'T' is not assignable to parameter of type 'string'.
  Type '"object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 100 more ... | ((props?: P, children?: Child[]) => Node)' is not assignable to type 'string'.
    Type '(props?: P, children?: Child[]) => Node' is not assignable to type 'string'.

466   const node = document.createElement(tag);
                                          ~~~

h.ts:470:28 - error TS2344: Type 'T' does not satisfy the constraint '"object" | "link" | "small" | "sub" | "sup" | "track" | "progress" | "a" | "abbr" | "address" | "applet" | "area" | "article" | "aside" | "audio" | "b" | "base" | "basefont" | "bdi" | ... 99 more ... | "wbr"'.
  Type 'T' is not assignable to type '"wbr"'.

470         ref = value as Ref<T>;
test("HTML without JSX or props", () => {
  const li = h("li");
  expect(li).toBeInstanceOf(HTMLLIElement);
});

test("Function without JSX with props", () => {
  function Div({ bar }: { bar: string }): HTMLDivElement {
    return <div className={bar} /> as HTMLDivElement;
  }
  const div = h(Div, { bar: "test" });
  expect(div).toBeInstanceOf(HTMLDivElement);
  expect(div.className).toBe("test");
});

test("Function without JSX or props", () => {
  function Div(): HTMLDivElement {
    return <div /> as HTMLDivElement;
  }
  const div = h(Div);
  expect(div).toBeInstanceOf(HTMLDivElement);
});