除了函数之外,TypeScript中是否有类型?

Zol*_*ási 8 typescript

我想表示一个参数应该是一个对象或一个简单的值类型(数字,bool,字符串等),但不是一个函数.

如果我使用Object,编译器让我分配一个函数.

var test: Object = () => "a";
Run Code Online (Sandbox Code Playgroud)

如果我使用any,当然也是同样的结果.在这种情况下,是否有类型或技巧可以帮助我?

我的基本目标是在使用Knockout observable时保证安全,这样我就不会忘记打开它们的那些小小的问题:)

Fen*_*ton 7

新功能答案

2018年11月添加-由于条件类型现在已成为问题!

条件类型为此提供了一种可能的解决方案,因为您可以创建NotFunction条件类型,如下所示:

type NotFunction<T> = T extends Function ? never : T;
Run Code Online (Sandbox Code Playgroud)

其工作原理如下:

const aFunction = (input: string) => input;
const anObject = { data: 'some data' };
const aString = 'data';

// Error function is not a never
const x: NotFunction<typeof aFunction> = aFunction;

// OK
const y: NotFunction<typeof anObject> = anObject;
const z: NotFunction<typeof aString> = aString;
Run Code Online (Sandbox Code Playgroud)

这样做的唯一缺点是,必须将变量放在语句的左侧和右侧-尽管如果您犯了以下错误,这是安全的,例如:

// Error - function is not a string
const x: NotFunction<typeof aString> = aFunction;
Run Code Online (Sandbox Code Playgroud)

原始答案

您可以使用进行运行时检查typeof,尽管这不是编译时检查,但会捕获那些您忘记执行功能的实例:

function example(input: any) {
    if (typeof input === 'function') {
        alert('You passed a function!');
    }
}

function someFunction() {
    return 1;
}

// Okay
example({ name: 'Zoltán' });
example(1);
example('a string');
example(someFunction());

// Not okay
example(function () {});
example(someFunction);
Run Code Online (Sandbox Code Playgroud)

为什么您实际上不能做您想要的?

您几乎可以做到,因为您可以使用重载来允许“多种类型之一”,例如:

class Example {
    someMethod(input: number);
    someMethod(input: string);
    someMethod(input: boolean);
    someMethod(input: any) {

    }
}
Run Code Online (Sandbox Code Playgroud)

麻烦之处在于:为了允许对象类型,您必须添加someMethod(input: Object);或的重载签名someMethod(input: {});。一旦执行此操作,就将允许使用函数,因为函数是从对象继承的。

如果您可以缩小object到不太通用的范围,则可以为要允许的所有类型简单地添加越来越多的重载(类似)。


Zig*_*ggy 7

您可以使用条件类型在TypeScript 2.8中执行此操作.

type NotFunc<T> = Exclude<T, Function>
function noFunc <T> (notF: T & NotFunc<T>) { return notF }

const f = () => 2

noFunc(f) // error!
noFunc(f()) // compiles!
Run Code Online (Sandbox Code Playgroud)

如果类型系统可以决定T扩展Function(即,T是一个函数)那么类型将是never,这是一个编译时错误.否则,类型将是T,您的代码将编译.

但是,您应该阅读此内容以更清楚地了解发生的情况:

// type NotFunc<T> = T extends Function ? never : T
type NotFunc<T> = Exclude<T, Function>

// the imporant cases for us are any and {}
type AnyNotFunc = NotFunc<any> // any
type ObjNotFunc = NotFunc<{}> // {}
type NullNotFunc = NotFunc<null> // never

// some functions, explicitly typed and inferred
const f: Function = () => 2
const ff = () => 2
const g: Function = (): null => null
const gg = (): null => null

// so a function like this won't work:
function badNoFunc <T> (notF: NotFunc<T>) { return notF }

// these all compile, because T is inferred as {} and NotFunc<{}> is just {}
badNoFunc(f)
badNoFunc(g)
badNoFunc(ff)
badNoFunc(gg)

// so need the T & NotFunc<T> to give the compiler a hint as to the type of T
function noFunc <T> (notF: T & NotFunc<T>) { return notF }

// now f is correctly inferred to be Function
noFunc(f) // error! f is assignable to Function
noFunc(g) // error! g is assignable to Function
noFunc(f()) // OK! 2 is not assignable to Function

// but we would expect g() === null to be never since NotFunc<null> === never
noFunc(g()) // OK? even though null is assignable to Function?
noFunc<null>(g()) // Error! Ah, type Function represents () => any but NotFunc<null> is never

// if we use the implicitly typed version, gg, the compiler infers the null return value correctly
noFunc(gg()) // Error! Correct

noFunc(ff) // error! The type is correctly inferred to be function
noFunc(gg) // error! The type is correctly inferred to be function
noFunc(ff()) // OK! 2 is not assignable to Function
Run Code Online (Sandbox Code Playgroud)

小贴士:

  1. OP可以做他们想做的事
  2. 该Function类型是一样糟糕any,所以要避免它


Ste*_*ich 5

这是一种定义所有有效(非函数)值然后使用递归定义的方法。我认为这适用于我的情况,并希望适用于遇到此问题的任何其他人。

Typescript Playground 示例

type NoFunctionValue =
    boolean
    | string
    | number
    | null
    | undefined
    | NoFunctionObject
    | NoFunctionArray

interface NoFunctionObject {
    [key: string]: NoFunctionValue
}

interface NoFunctionArray extends Array<NoFunctionValue> { }

// Try putting a function anywhere in here to see error
const text: NoFunctionObject = {
    bool: true,
    str: 'string',
    num: 7,
    nul: null,
    undef: undefined,
    arr: [true, 'string', 7, null, undefined],
    obj: {
        bool: true,
        str: 'string',
        num: 7,
        nul: null,
        undef: undefined,
        arr: [true, 'string', 7, null, undefined]
    }
} 
Run Code Online (Sandbox Code Playgroud)