问一个typescript 类型收窄问题

嘻倪孢
  • 334

tsconfig.json

{
"compilerOptions": {
    "outDir": "build/dist",
    "module": "esnext",
    "target": "esnext",
    "lib": ["esnext", "dom"],
    "sourceMap": true,
    "baseUrl": ".",
    "jsx": "react-jsx",
    "resolveJsonModule": true,
    "allowSyntheticDefaultImports": true,
    "moduleResolution": "node",
    "forceConsistentCasingInFileNames": true,
    "noImplicitReturns": true,
    "suppressImplicitAnyIndexErrors": true,
    "noUnusedLocals": true,
    "allowJs": true,
    "skipLibCheck": true,
    "experimentalDecorators": true,
    "strict": true,
    "paths": {
      "@/*": ["./src/*"],
      "@@/*": ["./src/.umi/*"],
    }
  }
}

eslintrc.js

module.exports = {
  extends: [require.resolve('@umijs/fabric/dist/eslint')],
  globals: {
    ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true,
    page: true,
    REACT_APP_ENV: true,
  },
};

更新问题

class A {
    readonly sdkType ='a'
    constructor () {

    }
}

class B {
    readonly sdkType ='b'
    constructor () {

    }
}

function test(this: A): 1;
function test(this: B): 2;
function test(this:A|B) {
    if (this instanceof A) {
        return 1
    } else {
        return 2
    }
}
// 这里在调用的时候又报错
const d = test.call(new A)

我的sdkType 就是做唯一类型判断的

原问题

function test<T extends string | number>(params: T): T extends number ? 1 : 2 {
      // 这里做了类型判断 
    if (typeof params === 'number') {
        // 这里报错了
         return 1
      } else {
        // 这里报错了
           return 2 
     }
}

回复
阅读 1.3k
3 个回答
type R<T> = T extends number ? 1 : 2;
function test<T extends string | number>(params: T): R<T> {
    if (typeof params === 'number') {
        return 1 as R<T>;
    } else {
        return 2 as R<T>;
    }
}

P.S. 这可读性挺差的,你这需求不如用函数重载了。


image.png

如果把参数声明为 this 参数,就会报这个错。但是如果是普通参数,就不会,看下图:

image.png

你看,其实也可以理解,作为 this 来说,是什么对像肯定是清楚的,不会是一个模棱两可的类型。

.call 的定义

    call<T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A): R;

这里 this 的类型 T 由 new A() 推导出来是 A,所以推导出来 callthis 类型,也就是 test 的类型,应该是 (this T, ...),也就是 (this A, ...),翻译成 interface 类型的接口就是

interface T {
    (this: A, ...args: any[]) => any;  // 不想去想返回类型了,就 any 吧
}

但实际 test 声明的是一个重载函数,其类型是

interface Test {
    (this: A, ...args: any[]) => 1;
    (this: B, ...args: any[]) => 2;
}

不管怎么说,单重载函数类型和多重载函数类型都是不一样的!

看看这篇:请别误用 TypeScript 重载函数类型

我觉得是不应该把逻辑和类型搞混的,该谁做的事情谁来做就好了。对于这个例子直接以下代码就足够了

function test(param: string|number): 1|2 {...}

你的思路更适合用在联合类型声明上,比如

type Test = {
    param: Extract<number|string, number>;
    value: 1
} | {
    param: Extract<number|string, string>;
    value: 2
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏