How to write type guards with no-unsafe-any enabled?

I don't know if something like no-unsafe-any buys you too much inside the implementation of a user-defined type guard, since usually the whole point of such a type guard is to allow the compiler to narrow values it can't normally do through the built-in control-flow narrowing. I'd certainly understand suspending a linter rule inside such an implementation.

But I think you can get nearly the behavior you're looking for like this:

function isIterable(obj: unknown): obj is Iterable<unknown> {
  if ((typeof obj !== 'object') || (obj === null)) return false; 
  // obj is now type object
  const wObj: { [Symbol.iterator]?: unknown } = obj; // safely widen to wObj
  return typeof wObj[Symbol.iterator] === 'function'; 
}

That's a few hoops to jump through, but the idea is to use control flow narrowing to narrow unknown to object, then widen object specifically to a type with an optional property you're trying to check (this happens by introducing a new variable). And finally, check the type of that property on the widened type. Since the property key you're checking is a symbol type, you need to mention the particular property name in the widened type. If the property key is a string, you can get away with using a string index signature:

function isPromise(obj: unknown): obj is Promise<unknown> {
  if ((typeof obj !== 'object') || (obj === null)) return false;
  // obj is now type object
  const wObj: {[k: string]: unknown} = obj; // safely widen to wObj
  return typeof wObj.then === 'function';
}

Anyway, I hope that gets you closer to your goal. Good luck!