In most cases, typedef's use could be easily replaced by a #define. However, it allows the use of standard declaration and greater flexibility in more complicated cases.

With typedef, you can create types that are arrays, which would be unwieldy to do with #define:
    typedef int arrayType[5];
    arrayType array1;
As opposed to:
    #define arrayMacro(var) int var [5]

Another difference is scope - in gcc, typedefs defined in a function are not valid outside a function. However, a #define will be valid from the point of its definition to the end of the file.
In Microsoft Visual C++, you cannot define a typedef inside a function.

Also, typedefs and variables can have identical names. This could lead to some confusion for others trying to read the code, though.